Current File : /var/www/e360ban/wp-content/plugins/wp-views/embedded/inc/wpv.class.php
<?php

// @todo check whethwe we can move this out of here

use const OTGS\Toolset\Views\UserCapabilities\EDIT_VIEWS;

require WPV_PATH_EMBEDDED . '/inc/listing/listing.php';


class WP_Views {

	/**
	 * @var bool[] Keys are View IDs, values determine if a View contains any enabled form controls.
	 *     Cache for self::does_view_have_form_controls().
	 */
	private $cache_view_ids_check_for_form_controls = array();

	/**
	 * @var bool[] Keys are View IDs, values determine if a View post is actually a WPA.
	 *     Cache for self::is_archive_view().
	 */
	private $is_archive_view = array();

	/**
	 * @var string[] Keys are View IDs, values include the query type of each View.
	 *     Cache for self::get_query_type().
	 */
	private $cache_query_type  = array();

	function __construct() {

		$this->view_ids								= array();
		$this->current_view							= null;
		$this->CCK_types 							= array();// @deprecated maybe
		$this->widget_view_id						= 0;
		$this->view_depth							= 0;
		$this->view_count							= array();
		$this->set_view_counts						= array();
		$this->view_shortcode_attributes			= array();
		$this->view_used_ids						= array();
		$this->rendering_views_form_in_progress		= false;

		$this->post_query							= null;
		$this->post_query_stack						= array();// @deprecated maybe
		$this->top_current_page						= null;
		$this->current_page							= array();

		$this->taxonomy_data						= array();
		$this->parent_taxonomy						= 0;

		$this->users_data							= array();
		$this->parent_user							= 0;

		$this->variables							= array();

		$this->force_disable_dependant_parametric_search	= false;
		$this->returned_ids_for_parametric_search			= array();

		add_action( 'after_setup_theme',								array( $this, 'before_init' ), 999 );

		add_action( 'init',												array( $this, 'init' ) );
		add_action( 'widgets_init',										array( $this, 'widgets_init' ) );

		add_action( 'init',												array( $this, 'wpv_register_assets' ) );

		// API
		add_filter( 'wpv_filter_wpv_get_current_view',					array( $this, 'wpv_get_current_view' ) );
		add_filter( 'wpv_filter_wpv_get_current_views_tree',			array( $this, 'wpv_get_current_views_tree' ) );
		add_action( 'wpv_action_wpv_set_current_view',					array( $this, 'wpv_set_current_view' ) );
		add_action( 'wpv_action_wpv_reset_current_view',				array( $this, 'wpv_reset_current_view' ) );

		// @todo move tis to a proper API file...
		add_filter( 'wpv_filter_wpv_get_object_unique_hash',			array( $this, 'wpv_get_object_unique_hash' ), 10, 2 );
		add_filter( 'wpv_filter_wpv_get_view_unique_hash',				array( $this, 'wpv_get_view_unique_hash' ) );

		// @todo move tis to a proper API file...
		add_filter( 'wpv_filter_wpv_get_object_settings',				array( $this, 'wpv_get_view_settings' ), 10, 3 );
		add_filter( 'wpv_filter_wpv_get_view_settings',					array( $this, 'wpv_get_view_settings' ), 10, 3 );
		add_filter( 'wpv_filter_wpv_get_object_layout_settings',		array( $this, 'wpv_get_view_layout_settings' ), 10, 2 );
		add_filter( 'wpv_filter_wpv_get_view_layout_settings',			array( $this, 'wpv_get_view_layout_settings' ), 10, 2 );

		add_filter( 'wpv_filter_wpv_get_query_type',					array( $this, 'wpv_get_query_type' ), 10, 2 );

		add_filter( 'wpv_filter_wpv_get_view_shortcodes_attributes',	array( $this, 'wpv_get_view_shortcodes_attributes' ) );
		add_action( 'wpv_action_wpv_set_view_shortcodes_attributes',	array( $this, 'set_view_shortcode_attributes' ) );
		add_action( 'wpv_action_wpv_reset_view_shortcodes_attributes',	array( $this, 'reset_view_shortcode_attributes' ) );

		add_filter( 'wpv_filter_wpv_get_max_pages',						array( $this, 'wpv_get_max_pages' ) );
		add_filter( 'wpv_filter_wpv_get_current_page_number',			array( $this, 'wpv_get_current_page_number' ) );

		add_filter( 'wpv_filter_wpv_get_top_current_post',				array( $this, 'wpv_get_top_current_post' ) );
		add_action( 'wpv_action_wpv_set_top_current_post',				array( $this, 'wpv_set_top_current_post' ) );

		/**
		 * Get the top current page for using it on the id="$current_page" shortcode attribute.
		 *
		 * @since 2.3.0
		 */
		add_filter( 'toolset_filter_get_top_current_post',				array( $this, 'wpv_get_top_current_post' ) );

		add_filter( 'wpv_filter_wpv_get_current_post',					array( $this, 'wpv_get_current_post' ) );
		add_action( 'wpv_action_wpv_set_current_post',					array( $this, 'wpv_set_current_post' ) );

		add_filter( 'wpv_filter_wpv_get_parent_view_taxonomy',			array( $this, 'wpv_get_parent_view_taxonomy' ) );
		add_action( 'wpv_action_wpv_set_parent_view_taxonomy',			array( $this, 'wpv_set_parent_view_taxonomy' ) );

		add_filter( 'wpv_filter_wpv_get_parent_view_user',				array( $this, 'wpv_get_parent_view_user' ) );
		add_action( 'wpv_action_wpv_set_parent_view_user',				array( $this, 'wpv_set_parent_view_user' ) );

		add_filter( 'wpv_filter_wpv_get_widget_view_id',				array( $this, 'wpv_get_widget_view_id' ) );
		add_action( 'wpv_action_wpv_set_widget_view_id',				array( $this, 'wpv_set_widget_view_id' ) );

		add_filter( 'wpv_filter_wpv_get_force_disable_dps',				array( $this, 'wpv_get_force_disable_dps' ) );
		add_action( 'wpv_action_wpv_force_disable_dps',					array( $this, 'wpv_force_disable_dps' ) );

		add_filter( 'wpv_filter_wpv_get_postmeta_keys',					array( $this, 'wpv_get_postmeta_keys' ), 10, 2 );
		add_filter( 'wpv_filter_wpv_get_termmeta_keys',					array( $this, 'wpv_get_termmeta_keys' ), 10, 2 );
		add_filter( 'wpv_filter_wpv_get_usermeta_keys',					array( $this, 'wpv_get_usermeta_keys' ), 10, 2 );

		add_filter( 'wpv_filter_wpv_get_rendered_views_ids',			array( $this, 'wpv_get_rendered_views_ids' ) );

		add_filter( 'wpv_filter_wpv_get_post_query',					array( $this, 'wpv_get_post_query' ) );
		add_filter( 'wpv_filter_wpv_get_taxonomy_query',				array( $this, 'wpv_get_taxonomy_query' ) );
		add_filter( 'wpv_filter_wpv_get_user_query',					array( $this, 'wpv_get_user_query' ) );

		add_filter( 'wpv_filter_wpv_get_taxonomy_found_count',			array( $this, 'wpv_get_taxonomy_found_count' ) );
		add_filter( 'wpv_filter_wpv_get_users_found_count',				array( $this, 'wpv_get_users_found_count' ) );

		add_filter( 'wpv_filter_wpv_is_rendering_form_view',			array( $this, 'wpv_is_rendering_form_view' ) );

		// PUBLIC API
		add_filter( 'wpv_filter_public_wpv_get_view_shortcodes_attributes',		array( $this, 'wpv_get_view_shortcodes_attributes' ) );

		// Filters the output of the View to produce "srcset" for the images in the View content.
		global $wp_version;
		if ( version_compare( $wp_version, '5.4.6', '<=' ) && is_callable( 'wp_make_content_images_responsive' ) ) {
			add_filter( 'wpv_filter_view_output', 'wp_make_content_images_responsive' );
		}
		if ( version_compare( $wp_version, '5.4.6', '>' ) && is_callable( 'wp_filter_content_tags' ) ) {
			add_filter( 'wpv_filter_view_output', 'wp_filter_content_tags' );
		}

	}


	function __destruct() { }

	// This happens on after_setup_theme:999
	function before_init() {
		/**
		 * Exclude filters.
		 * They need to be in place as soon as posible, since they are used by the Fields and Views dialog generation.
		 */
		// Exclude some taxonomies from different pieces of the GUI
		add_filter( 'wpv_admin_exclude_tax_slugs', 'wpv_admin_exclude_tax_slugs' );
		// Exclude some post types from different pieces of the GUI
		add_filter( 'wpv_admin_exclude_post_type_slugs', 'wpv_admin_exclude_post_type_slugs' );
        // Include some post types from different pieces of the GUI
        add_filter( 'wpv_admin_include_post_type_slugs', 'wpv_admin_include_post_type_slugs' );
	}

	function init() {

		

		$this->wpv_register_type_view();

		add_filter( 'toolset_filter_register_menu_pages',				array( $this, 'register_views_pages_in_menu' ), 40 );
		add_filter( 'toolset_filter_register_export_import_section',	array( $this, 'register_export_import_section' ), 20 );

		/*
		* ----------------------------
		* Assets
		* ----------------------------
		*/
		add_action( 'admin_enqueue_scripts', array( $this,'wpv_admin_enqueue_scripts' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'wpv_frontend_enqueue_scripts' ) );
		add_action( 'wpv_action_require_frontend_assets', array( $this, 'enqueue_optional_frontend_assets' ) );

		/*
		* ----------------------------
		* AJAX calls for date filters
		* @todo this might be better in the filter file
		* ----------------------------
		*/
		add_action('wp_ajax_wpv_format_date', array( $this, 'wpv_format_date' ) );
		add_action('wp_ajax_nopriv_wpv_format_date', array( $this, 'wpv_format_date' ) );

		/*
		* ----------------------------
		* Basic fallback values for get_view_settings and get_view_layout_settings
		* ----------------------------
		*/
		add_filter( 'wpv_view_settings', array( $this, 'wpv_view_settings_set_fallbacks' ), 5, 2 );
		add_filter( 'wpv_view_layout_settings', array( $this, 'wpv_view_layout_settings_set_fallbacks' ), 5, 2 );

		/*
		*
		* Extra loop wpv-item index management
		*
		*/
		add_filter( 'wpv_filter_wpv_item_loop_selected_index', array( $this, 'wpv_filter_wpv_item_loop_selected_index' ), 10, 2 );

		/*
		* ----------------------------
		* Workflows actions
		* ----------------------------
		*/

		// List the default spinners available for pagination and parametric search
		add_filter( 'wpv_admin_available_spinners', 'wpv_admin_available_spinners', 5 );

		// Delete the current user data after using it on a View listing users loop
		add_action( 'wpv-after-display-user', array( $this, 'clean_current_loop_user' ), 99 );

		// Manage the _toolset_edit_last postmeta on Views objects
		add_action( 'wpv_action_wpv_save_item', array( $this, 'after_save_item' ) );
		add_action( 'wpv_action_wpv_import_item', array( $this, 'after_import_item' ) );

		// Set priority lower than 5, so we know whether we need jQuery or not as early as possible
		add_action( 'wp_footer', array( $this, 'wpv_meta_html_extra_dependencies' ), 1 );
		// Set priority lower than 20, so we load the CSS before the footer scripts and avoid the bottleneck
		add_action( 'wp_footer', array( $this, 'wpv_meta_html_extra_css' ), 5 );
		// Set priority higher than 20, when all the footer scripts are loaded
		add_action( 'wp_footer', array( $this, 'wpv_meta_html_extra_js' ), 25 );
		// Set priority higher than 20, when all footer scripts are loaded, but before 25, when custom javascript is added
		add_action( 'wp_footer', array( $this, 'wpv_additional_js_files' ), 21 );

		if ( is_admin() ){
			add_filter( 'wpv_filter_get_meta_html_extra_css', array( $this, 'get_meta_html_extra_css' ) );
		}

		/*
		* ----------------------------
		* Compatibility
		* ----------------------------
		*/

		// WooCommerce
		add_filter( 'woocommerce_product_add_to_cart_url', array( $this, 'wpv_woocommerce_product_add_to_cart_url' ), 10, 2 );
		// Gravity Forms
		add_filter( 'gform_form_tag', array( $this, 'wpv_gravityforms_fix_form_action_on_ajax' ), 10, 2 );

		/*
		* ----------------------------
		* Shortcodes
		* ----------------------------
		*/
		add_shortcode( 'wpv-view', array( $this, 'short_tag_wpv_view' ) );
		add_shortcode( 'wpv-form-view', array( $this, 'short_tag_wpv_view_form' ) );

		add_filter( 'wpv_filter_wpv_view_shortcode_output', array( $this, 'remove_html_comments_from_shortcode_output' ) );
		add_filter( 'wpv_filter_wpv_view_shortcode_output', array( $this, 'trim_empty_characters_from_shortcode_output' ), 10, 2 );

		// Clear the WPV_Settings nstance when switching to another blog
		add_action( 'switch_blog', array( $this, 'wpv_clear_settings_instance' ) );

	}


	/**
	 * Register the post type of View.
	 *
	 * @since unknown
	 */
	function wpv_register_type_view() {
        $labels = array(
            'name' => _x( 'Views', 'post type general name' ),
            'singular_name' => _x( 'View', 'post type singular name' ),
            'add_new' => _x( 'Add New View', 'book' ),
            'add_new_item' => __( 'Add New View', 'wpv-views' ),
            'edit_item' => __( 'Edit View', 'wpv-views' ),
            'new_item' => __( 'New View', 'wpv-views' ),
            'view_item' => __( 'View Views', 'wpv-views' ),
            'search_items' => __( 'Search Views', 'wpv-views' ),
            'not_found' =>  __( 'No views found', 'wpv-views' ),
            'not_found_in_trash' => __( 'No views found in Trash', 'wpv-views' ),
            'parent_item_colon' => '',
            'menu_name' => 'Views'
        );
        $args = array(
            'labels' => $labels,
            'public' => false,
            'publicly_queryable' => false,
            'show_ui' => false,
            'show_in_menu' => false,
            'query_var' => false,
            'rewrite' => false,
            'can_export' => false,
            'capability_type' => 'post',
            'has_archive' => false,
            'hierarchical' => false,
            'supports' => array( 'title', 'editor', 'author' )
        );
        register_post_type( 'view', $args );
	}

	// Add WPML sync options.
	function language_options() {
		// not needed for theme version.
	}

	/*
	* ----------------------------
	* Clear the WPV_Settings instance when switching to another blog
	* ----------------------------
	*/

	function wpv_clear_settings_instance() {
		WPV_Settings::clear_instance();
	}

	/*
	* ----------------------------
	* Compatibility
	* ----------------------------
	*/

	/**
	* WooCommerce
	*
	* Fix malformed add to cart URL in Views AJAX pagination and automatic results in a parametric search.
	*
	* @see https://icanlocalize.basecamphq.com/projects/11629195-toolset-peripheral-work/todo_items/186738278/comments
	*/
	function wpv_woocommerce_product_add_to_cart_url( $add_to_cart_url, $wc_prod_object ) {
		if (
			defined( 'DOING_AJAX' )
			&& DOING_AJAX
			&& isset( $_REQUEST['action'] )
			&& (
				$_REQUEST['action'] == 'wpv_get_view_query_results'
				|| $_REQUEST['action'] == 'wpv_get_archive_query_results'
			)
		) {
			$parsed = array();
			$parsed = parse_url( $add_to_cart_url );
			if ( isset( $parsed['query'] ) ) {
				wp_parse_str( $parsed['query'], $parsed_query );

				// Only alter the URL if it already contains a numeric add-to-cart parameter
				if ( isset( $parsed_query['add-to-cart'] ) && is_numeric( $parsed_query['add-to-cart'] ) ) {

					// If the product is a variation, we need to handle the variation_id parameter too
					if ( isset( $wc_prod_object->product_type )
						&& $wc_prod_object->product_type == 'variation'
						&& isset( $wc_prod_object->variation_id )
						&& isset( $wc_prod_object->variation_data )
					) {
						$query_args_to_add = array_merge( array( 'variation_id' => $wc_prod_object->variation_id ), $wc_prod_object->variation_data );
					} else {
						$query_args_to_add = array();
					}

					// Build the base URL, it should have a referrer being the actual current page
					if ( wp_get_referer() ) {
						$base_url = wp_get_referer();
					} else {
						$base_url = get_home_url();
					}

					// Modify the URL
					$query_args_to_add['add-to-cart'] = $parsed_query['add-to-cart'];
					$add_to_cart_url = esc_url( remove_query_arg( 'added-to-cart', add_query_arg( $query_args_to_add, $base_url ) ) );
				}
			}
		}
		return $add_to_cart_url;
	}

	/**
	* Gravity Forms
	*
	* wpv_gravityforms_fix_form_action_on_ajax
	*
	* Fix the action attribute for Gravity Forms loaded on Views AJAX calls
	*
	* @since 1.10
	*/

	function wpv_gravityforms_fix_form_action_on_ajax( $form_tag, $form ) {
		if ( preg_match( "|action='(.*?)'|", $form_tag, $matches ) ) {
			$form_action = $matches[1];
			if (
				defined( 'DOING_AJAX' )
				&& DOING_AJAX
				&& isset( $_REQUEST['action'] )
				&& (
					$_REQUEST['action'] == 'wpv_get_view_query_results'
					|| $_REQUEST['action'] == 'wpv_get_archive_query_results'
				)
			) {
				$base_url = "action='" . esc_url( wp_get_referer() ) . "'";
				$form_tag = preg_replace( "|action='(.*?)'|", $base_url, $form_tag );
			}
		}
		return $form_tag;
	}

	/**
	 * Register Views widgets
	 *
	 * @since unknown
	 */
	function widgets_init() {
		register_widget( 'WPV_Widget' );
		register_widget( 'WPV_Widget_filter' );
	}


	function register_CCK_type( $type ) {
		$this->CCK_types[] = $type;
	}


	function can_include_type($type) {
		return !in_array( $type, $this->CCK_types );
	}

	function register_views_pages_in_menu( $pages ) {
		if ( $this->is_embedded() ) {
			$page = wpv_getget( 'page' );
			$pages[] = array(
				'slug'			=> 'embedded-views',
				'menu_title'	=> __( 'Views', 'wpv-views' ),
				'page_title'	=> __( 'Views', 'wpv-views' ),
				'callback'		=> 'wpv_admin_menu_embedded_views_listing_page',
				'capability' => EDIT_VIEWS,
			);
			if ( 'views-embedded' == $page ) {
				add_filter( 'screen_options_show_screen', '__return_false', 99 );
				$pages[] = array(
					'slug'			=> 'views-embedded',
					'menu_title'	=> __( 'Embedded View', 'wpv-views' ),
					'page_title'	=> __( 'Embedded View', 'wpv-views' ),
					'callback'		=> 'views_embedded_html',
					'capability' => EDIT_VIEWS,
				);
			}
			$pages[] = array(
				'slug'			=> 'embedded-views-templates',
				'menu_title'	=> __( 'Content Templates', 'wpv-views' ),
				'page_title'	=> __( 'Content Templates', 'wpv-views' ),
				'callback'		=> 'wpv_admin_menu_embedded_views_templates_listing_page',
				'capability' => EDIT_VIEWS,
			);
			if ( 'view-templates-embedded' == $page ) {
				add_filter( 'screen_options_show_screen', '__return_false', 99 );
				$pages[] = array(
					'slug'			=> 'view-templates-embedded',
					'menu_title'	=> __( 'Embedded Content Template', 'wpv-views' ),
					'page_title'	=> __( 'Embedded Content Template', 'wpv-views' ),
					'callback'		=> 'content_templates_embedded_html',
					'capability' => EDIT_VIEWS,
				);
			}
			$pages[] = array(
				'slug'			=> 'embedded-views-archives',
				'menu_title'	=> __( 'WordPress Archives', 'wpv-views' ),
				'page_title'	=> __( 'WordPress Archives', 'wpv-views' ),
				'callback'		=> 'wpv_admin_menu_embedded_views_archives_listing_page',
				'capability' => EDIT_VIEWS,
			);
			if ( 'view-archives-embedded' == $page ) {
				add_filter( 'screen_options_show_screen', '__return_false', 99 );
				$pages[] = array(
					'slug'			=> 'view-archives-embedded',
					'menu_title'	=> __( 'Embedded WordPress Archive', 'wpv-views' ),
					'page_title'	=> __( 'Embedded WordPress Archive', 'wpv-views' ),
					'callback'		=> 'view_archives_embedded_html',
					'capability' => EDIT_VIEWS,
				);
			}
		}
		return $pages;
	}

	function register_export_import_section( $sections ) {
		$icon_classname = ( 'blocks' === wpv_get_views_flavour() ) ? 'icon-toolset-blocks' : 'icon-views-logo';
		$promo_link_args = array(
			'query'		=> array(
				'utm_source'	=> 'plugin',
				'utm_campaign'	=> 'views',
				'utm_medium'	=> 'gui',
				'utm_term'		=> 'Get Views'

			),
			'anchor'	=> 'views'
		);
		$promo_link = WPV_Admin_Messages::get_documentation_promotional_link( $promo_link_args, 'https://toolset.com/home/toolset-components/' );
		$sections['wpv-views'] = array(
			'slug'		=> 'wpv-views',
			'title'		=> __( 'Views', 'wpv-views' ),
			'icon'		=> '<i class="' . esc_attr( $icon_classname ) . ' ont-icon-16"></i>',
			'items'		=> array(
				'mixed'	=> array(
								'title'		=> __( 'Export and Import Views data', 'wpv-views' ),
								'content'	=> '<p>'
											. __( 'You need the full Toolset Views plugin to export and import data.', 'wpv-views' )
											. WPV_MESSAGE_SPACE_CHAR
											. '<a href="' . $promo_link . '" title="" class="button button-primary-toolset" target="_blank">'
											. __( 'Get Views', 'wpv-views' )
											. '</a>'
											. '</p>'
							)
			)
		);
		return $sections;
	}

	// @deprecate
	// enqueue this correctly
	function settings_box_load() {
		global $pagenow;
		if ( $pagenow == 'options-general.php' && isset( $_GET['page'] ) && $_GET['page'] == 'wpv-import-theme' ) {
			$this->include_admin_css();
		}
	}


	function include_admin_css() {
		printf(
				'<link rel="stylesheet" href="%s" type="text/css" media="all" />',
				esc_url( add_query_arg( array( 'v' => WPV_VERSION ), WPV_URL . '/res/css/wpv-views.css' ) ) );
	}

	/*
	* after_save_item
	* after_import_item
	*
	* Manage the _toolset_edit_last postmeta on Views objects
	*/

	function after_save_item( $item_id ) {
		// do nothing in the embedded version
	}

	function after_import_item( $item_id ) {
		if (
			! is_numeric( $item_id )
			|| intval( $item_id ) < 1
		) {
			return;
		}
        delete_post_meta( $item_id, '_toolset_edit_last' );
	}

    /**
     * Return View ID given the slug or the name
     * @param type $atts shortcode attributes
     * @return type int View ID
     */
	function get_view_id( $atts ) {
		global $wpdb;
		extract(
			shortcode_atts(
				array(
					'id'	=> false,
					'name'  => false
				),
				$atts
			)
		);

		if (
			empty( $id )
			&& ! empty( $name )
		) {
			// lookup by post title first
			$id = $wpdb->get_var(
				$wpdb->prepare(
					"SELECT ID FROM {$wpdb->posts}
					WHERE post_type = 'view'
					AND post_status != 'draft'
					AND post_title = %s
					LIMIT 1",
					$name
				)
			);
			if ( ! $id ) {
				// try the post name
				$id = $wpdb->get_var(
					$wpdb->prepare(
						"SELECT ID FROM {$wpdb->posts}
						WHERE post_type = 'view'
						AND post_status != 'draft'
						AND post_name = %s
						LIMIT 1",
						$name
					)
				);
			}
		}

		return $id;
	}


	/**
 	 * Process the View shortcode.
	 *
	 * eg. [wpv-view name='my-view']
	 */
	function short_tag_wpv_view( $atts ) {
		$atts = toolset_ensarr( $atts );

		toolset_wplog( $atts, null, __FILE__, 'short_tag_wpv_view', 541 );

		apply_filters( 'wpv_shortcode_debug', 'wpv-view', json_encode($atts), '' , 'Output shown in the Nested elements section' );

		$id = $this->get_view_id( $atts );

		if( empty( $id ) ) {
			return sprintf( '<!- %s ->', __( 'View not found', 'wpv-views' ) );
		}

		$this->view_used_ids[] = $id;

		do_action( 'wpv_action_require_frontend_assets' );

        array_push( $this->view_shortcode_attributes, $atts );

        // Shall we look up in the cache?
		$is_cacheable = $this->is_cacheable( $id, $atts );
		if ( $is_cacheable ) {
			$views_cache_store = \OTGS\Toolset\Views\Controller\Cache\Views\Store::get_instance();
			if ( 'layout' === toolset_getarr( $atts, 'view_display' ) ) {
				$cached_outcome = $views_cache_store->get_loop_cache( $id );
			} else {
				$cached_outcome = $views_cache_store->get_full_cache( $id );
			}

			if ( false !== $cached_outcome ) {
				array_pop( $this->view_shortcode_attributes );
				return apply_filters( 'wpv_filter_wpv_view_shortcode_output', $cached_outcome, $id );
			}
		}

		$out = $this->render_view_ex( $id, md5( serialize( $atts ) ) );

        // Update Views cache if applicable
        if ( $is_cacheable ) {
			if ( 'layout' === toolset_getarr( $atts, 'view_display' ) ) {
				$views_cache_store->set_loop_cache( $id, $out );
			} else {
				$views_cache_store->set_full_cache( $id, $out );
			}
		}

		array_pop( $this->view_shortcode_attributes );
		$out = apply_filters( 'wpv_filter_wpv_view_shortcode_output', $out, $id );

		return $out;

	}


	/**
	* Process the View form shortcode.
	*
	* eg. [wpv-form-view name='my-view' target_id='xx']
	*
	* @todo Switch .js-wpv-filter-data-for-this-form into a form data attribute that we can init on document.ready and refresh when neeeded
	*/
	function short_tag_wpv_view_form( $atts ) {
		$atts = toolset_ensarr( $atts );

		global $sitepress;

		toolset_wplog( $atts, null, __FILE__, 'short_tag_wpv_view_form', 610 );

		apply_filters( 'wpv_shortcode_debug', 'wpv-form-view', json_encode($atts), '', 'Output shown in the Nested elements section' );

		extract( shortcode_atts(
			array(
					'id' => false,
					'name' => false,
					'target_id' => 'self'
			),
			$atts )
		);

		$id = $this->get_view_id( $atts );

		if( empty( $id ) ) {
			return sprintf( '<!- %s ->', __( 'View not found', 'wpv-views' ) );
		}

		if (
			empty( $target_id )
			|| $target_id == 'self'
		) {
			$target_id = 'self';
			$url = $_SERVER['REQUEST_URI'];
			// @todo review this, we might do the same than on wpv-filter-embedded.php:
			/*
			* $view_settings			= apply_filters( 'wpv_filter_wpv_get_view_settings', array() );
			* $pagination_permalinks	= apply_filters( 'wpv_filter_wpv_get_pagination_permalinks', array(), $view_settings, $view_id );
			* $url						= $pagination_permalinks['first'];
			*/
			if (
				defined('DOING_AJAX')
				&& DOING_AJAX
				&& isset( $_REQUEST['action'] )
				&& (
					$_REQUEST['action'] == 'wpv_get_view_query_results'
					|| $_REQUEST['action'] == 'wpv_get_archive_query_results'
				)
			) {
				if ( wp_get_referer() ) {
					$url = wp_get_referer();
				}
			}
			if (
				isset( $_GET['wpv_aux_current_post_id'] )
				&& is_numeric( $_GET['wpv_aux_current_post_id'] )
			) {
				$url = get_permalink( intval( $_GET['wpv_aux_current_post_id'] ) );
			}
		} else {
			if ( is_numeric( $target_id ) ) {
				// Adjust for WPML support
				$target_id = apply_filters( 'translate_object_id', $target_id, 'page', true, null );
				$url = get_permalink( $target_id );
			} else {
				return sprintf( '<!- %s ->', __( 'target_id not valid', 'wpv-views' ) );
			}
		}

		$this->view_used_ids[] = $id;

		do_action( 'wpv_action_require_frontend_assets' );

        array_push( $this->view_shortcode_attributes, $atts );

        // Shall we look up in the cache? Is this a Parametric Search View?
        $is_cacheable = (
			$this->is_cacheable( $id, $atts )
			&& $this->does_view_have_form_controls( $id )
		);
		if ( $is_cacheable ) {
			$views_cache_store = \OTGS\Toolset\Views\Controller\Cache\Views\Store::get_instance();
			$cached_outcome = $views_cache_store->get_form_cache( $id );

			if ( false !== $cached_outcome ) {
				array_pop( $this->view_shortcode_attributes );
				return $cached_outcome;
			}
		}

		$this->rendering_views_form_in_progress = true;

		$out = '';

		$view_settings = $this->get_view_settings( $id );

		/**
		 * Adjust the View settings just before getting the allowed URL parameters,
		 * to include the JIT defined query filters as companion for search filters.
		 *
		 * @param array $view_settings
		 * @return array
		 * @since 3.0
		 */
		$view_settings = apply_filters( 'wpv_filter_object_settings_for_fake_url_query_filters', $view_settings );

		if ( isset( $view_settings['filter_meta_html'] ) ) {

            $this->view_depth++;
			array_push( $this->view_ids, $this->current_view );
			$this->current_view = $id;

			// increment the view count.
			if ( !isset( $this->view_count[ $this->view_depth ] ) ) {
				$this->view_count[ $this->view_depth ] = 0;
			}
			$this->view_count[ $this->view_depth ]++;

			$form_class = array( 'js-wpv-form-only' );

			// Dependant stuff
			$dps_enabled = false;
			$counters_enabled = false;
			if ( !isset( $view_settings['dps'] ) || !is_array( $view_settings['dps'] ) ) {
				$view_settings['dps'] = array();
			}
			if ( isset( $view_settings['dps']['enable_dependency'] ) && $view_settings['dps']['enable_dependency'] == 'enable' ) {
				$dps_enabled = true;
				$controls_per_kind = wpv_count_filter_controls( $view_settings );
				$controls_count = 0;
				$no_intersection = array();
				if ( !isset( $controls_per_kind['error'] ) ) {
					// $controls_count = array_sum( $controls_per_kind );
					$controls_count = $controls_per_kind['cf'] + $controls_per_kind['tax'] + $controls_per_kind['pr'] + $controls_per_kind['search'];
					if ( $controls_per_kind['cf'] > 1 && ( !isset( $view_settings['custom_fields_relationship'] ) || $view_settings['custom_fields_relationship'] != 'AND' ) ) {
						$no_intersection[] = __( 'custom field', 'wpv-views' );
					}
					if ( $controls_per_kind['tax'] > 1 && ( !isset( $view_settings['taxonomy_relationship'] ) || $view_settings['taxonomy_relationship'] != 'AND' ) ) {
						$no_intersection[] = __( 'taxonomy', 'wpv-views' );
					}
				} else {
					$dps_enabled = false;
				}
				if ( $controls_count > 0 ) {
					if ( count( $no_intersection ) > 0 ) {
						$dps_enabled = false;
					}
				} else {
					$dps_enabled = false;
				}
			}
			if ( !isset( $view_settings['filter_meta_html'] ) ) {
				$view_settings['filter_meta_html'] = '';
			}
			if ( strpos( $view_settings['filter_meta_html'], '%%COUNT%%' ) !== false ) {
				$counters_enabled = true;
			}
			if ( $dps_enabled || $counters_enabled ) {
				// TODO review this, makes little sense
				if ( $dps_enabled ) {
					$form_class[] = 'js-wpv-dps-enabled';
				}
				do_action( 'wpv_action_extend_query_for_parametric_and_counters', array(), $view_settings, $id );
			} else {
				// Set the force value
				$this->set_force_disable_dependant_parametric_search( true );
			}

			if ( ! isset( $view_settings['dps']['ajax_results'] ) ) {
				$view_settings['dps']['ajax_results'] = 'disable';
			}
			if ( ! isset( $view_settings['dps']['ajax_results_submit'] ) ) {
				$view_settings['dps']['ajax_results_submit'] = 'reload';
			}
			$ajax = $view_settings['dps']['ajax_results'];
			$ajax_submit = $view_settings['dps']['ajax_results_submit'];
			// Disable AJAX results when the target page is set and is not the current one, since there should be no results here whatsoever
			// (and if there are, they belong to a page that should not be targeted by this form)
			$current_page = $this->get_top_current_page();
			if (
				$target_id != 'self'
				&& (
					! $current_page
					|| $current_page->ID != $target_id
				)
			) {
				$ajax = 'disable';
				$ajax_submit = 'reload';
			}

			if ( $ajax == 'enable' ) {
				$form_class[] = 'js-wpv-ajax-results-enabled';
			} else if ( $ajax == 'disable' && $ajax_submit == 'ajaxed' ) {
				$form_class[] = 'js-wpv-ajax-results-submit-enabled';
			}

			$page = 1;

			$effect = 'fade';
			$ajax_pre_before = '';
			if ( isset( $view_settings['dps']['ajax_results_pre_before'] ) ) {
				$ajax_pre_before = esc_attr( $view_settings['dps']['ajax_results_pre_before'] );
			}
			$ajax_before = '';
			if ( isset( $view_settings['dps']['ajax_results_before'] ) ) {
				$ajax_before = esc_attr( $view_settings['dps']['ajax_results_before'] );
			}
			$ajax_after = '';
			if ( isset( $view_settings['dps']['ajax_results_after'] ) ) {
				$ajax_after = esc_attr( $view_settings['dps']['ajax_results_after'] );
			}

			//$url = get_permalink($target_id);
			if( isset( $sitepress ) ) {
				// Dirty hack to be able to use the wpml_content_fix_links_to_translated_content() function
				// It will take a string, parse its links based on <a> tag and return the translated link

				// @todo this is not needed anymore, we already translate the $url above
				// on the only case it is a permalink to a given post ID
				$url = '<a href="' . esc_url( $url ) . '"></a>';
				$url = wpml_content_fix_links_to_translated_content( $url );
				$url = substr( $url, 9, -6 );
			}

            $view_url_data			= get_view_allowed_url_parameters( $id );
            $query_args_remove		= wp_list_pluck( $view_url_data, 'attribute' );

            $url = remove_query_arg(
                $query_args_remove,
                $url
            );

			$view_attrs = $atts;

			$sort_orderby		= '';
			$sort_order			= '';
			$sort_orderby_as	= '';
			$sort_orderby_second	= '';
			$sort_order_second		= '';

			if (
				isset( $_GET['wpv_view_count'] )
				&& esc_attr( $_GET['wpv_view_count'] ) == $this->get_view_count()
			) {
				if (
					isset( $_GET['wpv_sort_orderby'] )
					&& esc_attr( $_GET['wpv_sort_orderby'] ) != ''
				) {
					$sort_orderby = esc_attr( $_GET['wpv_sort_orderby'] );
				}
				if (
					isset( $_GET['wpv_sort_order'] )
					&& esc_attr( $_GET['wpv_sort_order'] ) != ''
				) {
					$sort_order = esc_attr( $_GET['wpv_sort_order'] );
				}
				if (
					isset( $_GET['wpv_sort_orderby_as'] )
					&& esc_attr( $_GET['wpv_sort_orderby_as'] ) != ''
				) {
					$sort_orderby_as = esc_attr( $_GET['wpv_sort_orderby_as'] );
				}
				// Secondary sorting
				if (
					isset( $_GET['wpv_sort_order_second'] )
					&& in_array( strtoupper( esc_attr( $_GET['wpv_sort_order_second'] ) ), array( 'ASC', 'DESC' ) )
				) {
					$sort_order_second = strtoupper( esc_attr( $_GET['wpv_sort_order_second'] ) );
				}
				if (
					isset( $_GET['wpv_sort_orderby_second'] )
					&& esc_attr( $_GET['wpv_sort_orderby_second'] ) != 'undefined'
					&& esc_attr( $_GET['wpv_sort_orderby_second'] ) != ''
					&& in_array( $_GET['wpv_sort_orderby_second'], array( 'post_date', 'post_title', 'ID', 'modified', 'menu_order', 'rand' ) )
				) {
					$sort_orderby_second = esc_attr( $_GET['wpv_sort_orderby_second'] );
				}
			}

			// @todo Switch .js-wpv-filter-data-for-this-form into a form data attribute that we can init on document.ready and refresh when neeeded
			// @note Mind that this could be served from cache, so we better have a caching cleaering mechanism that runs only once...

			$parametric_data = array(
				'query'				=> 'normal',
				'id'				=> $id,
				'view_id'			=> $id,
				'widget_id'			=> $this->get_widget_view_id(),
				'view_hash'			=> $this->get_view_count(),
				'action'			=> esc_url( $url ),
				'sort'				=> array(
									'orderby'		=> $sort_orderby,
									'order'			=> $sort_order,
									'orderby_as'	=> $sort_orderby_as,
									'orderby_second'	=> $sort_orderby_second,
									'order_second'		=> $sort_order_second,
									),
				'orderby'			=> $sort_orderby,
				'order'				=> $sort_order,
				'orderby_as'		=> $sort_orderby_as,
				'orderby_second'	=> $sort_orderby_second,
				'order_second'		=> $sort_order_second,
				'ajax_form'			=> '',// 'disabled'|'enabled'
				'ajax_results'		=> '',// 'disabled'|'onsubmit'|'enabled'
				'effect'			=> 'fade',
				'prebefore'			=> $ajax_pre_before,
				'before'			=> $ajax_before,
				'after'				=> $ajax_after
			);

			$view_attrs_to_keep = $view_attrs;
			if ( isset( $view_attrs_to_keep['name'] ) ) {
				unset( $view_attrs_to_keep['name'] );
			}
			if ( isset( $view_attrs_to_keep['target_id'] ) ) {
				unset( $view_attrs_to_keep['target_id'] );
			}

			$parametric_data['attributes'] = $view_attrs_to_keep;

			$view_auxiliar_requires = array(
				'current_post_id'	=> 0,
				'parent_post_id'	=> 0,
				'parent_term_id'	=> 0,
				'parent_user_id'	=> 0
			);

			// Fill environmental data for AJAXed operations:
			// Top current post
			$top_current_post = $this->get_top_current_page();
			if (
				$top_current_post
				&& isset( $top_current_post->ID )
			) {
				$view_auxiliar_requires['current_post_id'] = $top_current_post->ID;
			}
			// Parent post
			$current_post = $this->get_current_page();
			if (
				$current_post
				&& isset( $current_post->ID )
			) {
				$view_auxiliar_requires['parent_post_id'] = $current_post->ID;
			}
			// Parent term
			$parent_term_id = $this->get_parent_view_taxonomy();
			if ( $parent_term_id ) {
				$view_auxiliar_requires['parent_term_id'] = $parent_term_id;
			}
			// Parent user
			$parent_user_id = $this->get_parent_view_user();
			if ( $parent_user_id ) {
				$view_auxiliar_requires['parent_user_id'] = $parent_user_id;
			}

			$archive_environment = apply_filters( 'wpv_filter_wpv_get_current_archive_loop', array() );
			$view_auxiliar_requires['archive'] = array(
				'type'	=> $archive_environment['type'],
				'name'	=> $archive_environment['name'],
				'data'	=> $archive_environment['data'],
			);

			$parametric_data['environment'] = $view_auxiliar_requires;

			$parametric_data = apply_filters( 'wpv_filter_wpv_get_parametric_settings', $parametric_data, $view_settings );

			$out .= '<form'
				. ' autocomplete="off"'
				. ' action="' . esc_url( $url ) . '"'
				. ' method="get"'
				. ' class="wpv-filter-form js-wpv-filter-form js-wpv-filter-form-' . $this->get_view_count() . ' ' . implode( ' ', $form_class ) . '"'
				. ' data-viewnumber="' . $this->get_view_count() . '"'
				. ' data-targetid="' . $target_id . '"'
				. ' data-viewid="' . $id . '"'
				. ' data-viewhash="' . base64_encode( json_encode( $view_attrs ) ) . '"'
				. ' data-viewwidgetid="' . intval( $this->get_widget_view_id() ) . '"'
				. ' data-orderby="' . $sort_orderby . '"'
				. ' data-order="' . $sort_order . '"'
				. ' data-orderbyas="' . $sort_orderby_as . '"'
				. ' data-orderbysecond="' . $sort_orderby_second . '"'
				. ' data-ordersecond="' . $sort_order_second . '"'
				. ' data-parametric="' . esc_js( wp_json_encode( $parametric_data ) ) . '"'
				. ' data-attributes="' . esc_js( wp_json_encode( $view_attrs_to_keep ) ) . '"'
				. ' data-environment="' . esc_js( wp_json_encode( $view_auxiliar_requires ) ) . '"'
				. '>';

			$out .= '<input'
				. ' type="hidden"'
				. ' class="js-wpv-dps-filter-data js-wpv-filter-data-for-this-form"'
				. ' data-action="' . esc_url( $url ) . '"'
				. ' data-page="' . $page . '"'
				. ' data-ajax="disable"'
				. ' data-effect="' . $effect . '"'
				. ' data-ajaxprebefore="' . $ajax_pre_before . '"'
				. ' data-ajaxbefore="' . $ajax_before . '"'
				. ' data-ajaxafter="' . $ajax_after . '"'
				. ' />';

			// add hidden inputs for any url parameters.
			// We need these for when the form is submitted.
			$url_query = parse_url( $url, PHP_URL_QUERY );
			if ( $url_query != '' ) {
				$url_query_args = wp_parse_args( $url_query );
				$out .= wpv_filter_recursive_add_extra_parameters( $url_query_args );
			}

			/**
			 * Add a hidden field for the View count for multiple Views per page.
			 */
			$out .= '<input class="' . esc_attr( 'wpv_view_count wpv_view_count-' . $this->get_view_count() ) . '" type="hidden" name="wpv_view_count" value="' . esc_attr( $this->get_view_count() ) . '" />';

			$view_id = $id;
			$is_required = true;

			/**
			 * Filter wpv_filter_start_filter_form
			 *
			 * @param $out the default form opening tag followed by the required hidden input tags needed for pagination and table sorting
			 * @param $view_settings the current View settings
			 * @param $view_id the ID of the View being displayed
			 * @param $is_required [true|false] whether this View requires a form to be displayed (has a parametric search OR uses table sorting OR uses pagination)
			 *
			 * This can be useful to create additional inputs for the current form without needing to add them to the Filter HTML textarea
			 * Also, can help users having formatting issues
			 *
			 * @return $out
			 *
			 * Since 2.3.0
			 *
			 */
			$out = apply_filters( 'wpv_filter_start_filter_form', $out, $view_settings, $view_id, $is_required );

			$meta_html = $this->translate_view_form( $view_settings['filter_meta_html'], $id );
			$fixmatches = '';

			if(	preg_match( '#\\[wpv-filter-start.*?\](.*?)\\[\wpv-filter-end\\]#is', $meta_html, $matches ) ) {

				$fixmatches = str_replace( ' hide="true"', '', $matches[1] );

			} else if( preg_match( '#\\[wpv-filter-controls\\](.*?)\\[\/wpv-filter-controls\\]#is', $meta_html, $matches ) ) {

				$fixmatches = str_replace( ' hide="true"', '', $matches[0] );

			} elseif( preg_match( '#\\[wpv-control.*?\\]#is', $meta_html ) || preg_match( '#\\[wpv-filter-search-box.*?\]#is', $meta_html ) ) {

				if(	preg_match( '#\\[wpv-filter-start.*?\](.*?)\\[\wpv-filter-end\\]#is', $meta_html, $matches ) ) {
					$fixmatches = str_replace( ' hide="true"', '', $matches[1] );
				}
			}

			$out .= wpv_do_shortcode( $fixmatches );

			$form_closure = '</form>';

			/**
			 * Filter wpv_filter_end_filter_form
			 *
			 * @param $out the default form closing tag
			 * @param $view_settings the current View settings
			 * @param $view_id the ID of the View being displayed
			 * @param $is_required [true|false] whether this View requires a form to be displayed (has a parametric search OR uses table sorting OR uses pagination)
			 *
			 * This can be useful to create additional inputs for the current form without needing to add them to the Filter HTML textarea
			 *
			 * @return $out
			 *
			 * Since 2.3.0
			 *
			 */
			$form_closure = apply_filters( 'wpv_filter_end_filter_form', $form_closure, $view_settings, $view_id, $is_required );


			$out .= $form_closure;

			$this->current_view = array_pop( $this->view_ids );
			$this->view_depth--;
		}

		// Update Views cache if applicable
		if ( $is_cacheable ) {
			$views_cache_store->set_form_cache( $id, $out );
		}

		array_pop( $this->view_shortcode_attributes );

		$this->rendering_views_form_in_progress = false;

		return $out;
	}


	/**
	 * Returns a form translation if it exists, otherwise the original language form is returned.
	 *
	 * This should not be here, but rendering only a form of a View is completely separated from rendering a view.
	 * It would first require a refactoring of the form rendering to use the new translation mechanic.
	 *
	 * @param string $original_language_form
	 * @param string|int $view_id
	 *
	 * @return string
	 */
	private function translate_view_form( $original_language_form, $view_id ) {
		$current_language = apply_filters( 'wpml_current_language', false );
		if ( ! $current_language ) {
			// WPML not active.
			return $original_language_form;
		}

		$default_language = apply_filters( 'wpml_default_language', false );

		if ( $current_language === $default_language ) {
			// Visitor uses default language => nothing to translate.
			return $original_language_form;
		}

		// Get the post where the view was created
		$used_in_posts = get_post_meta( (int) $view_id, '_wpv_used_in_posts', true );

		if ( empty( $used_in_posts ) ) {
			// This is true for legacy views. These are not supported (yet).
			return $original_language_form;
		}

		// The _wpv_used_in_posts is stored as a string in this format: [1957,1964,2004].
		$used_in_posts = trim( $used_in_posts, '[]' );
		$used_in_posts = explode( ',', $used_in_posts );

		$post_with_view = get_post( (int) $used_in_posts[0] );
		if ( ! $post_with_view ) {
			// Post, where the view was created, not found.
			// This is needed as the translation of that post holds the translation.
			return $original_language_form;
		}

		// Get translation.
		$translated_post_id = apply_filters( 'wpml_object_id', $post_with_view->ID, $post_with_view->post_type );
		if ( ! $translated_post_id ) {
			// The view was not translated. It must be translated where it was created and cannot be translated
			// on the post where it's just inserted via View Block -> Existing View.
			return $original_language_form;
		}

		$translated_post = get_post( $translated_post_id );
		if ( ! $translated_post ) {
			// This shouldn't happen. There is an translated id, but no post behind it. When this happens the site
			// was probably badly ported.
			return $original_language_form;
		}

		// Get search container.
		$block_start = '<!-- wp:toolset-views/custom-search-container';
		$block_end = '<!-- /wp:toolset-views/custom-search-container -->';

		$view_search_start_position = strpos( $translated_post->post_content, $block_start );

		if ( false === $view_search_start_position ) {
			// No view search in the translation.
			return $original_language_form;
		}

		// Check for another search container.
		// When the original post, which contains the inserted view form, has more than one search container, the
		// translation is aborted. This probably never happens and the logic to find the right container would be nasty
		// as there is no related id which can be checked. If this will become an requirement in the future, the best
		// approach would probably be comparing the blocks involved and check that the attributes are the same, to
		// determine the right search container.
		$more_than_on_view_search = strpos(
			$translated_post->post_content,
			$block_start,
			$view_search_start_position + strlen( $block_start )
		);

		if ( false !== $more_than_on_view_search ) {
			return $original_language_form;
		}

		// Get end position of search container.
		$view_search_end_position = strpos( $translated_post->post_content, $block_end );

		if ( false === $view_search_end_position ) {
			// Broken block.
			return $original_language_form;
		}

		// Translation available. Determine the true end position
		$view_search_string_length = $view_search_end_position + strlen( $block_end ) - $view_search_start_position;
		return substr(
			$translated_post->post_content,
			$view_search_start_position,
			$view_search_string_length
		);
	}

	function remove_html_comments_from_shortcode_output( $out ) {
		$out = str_replace('<!-- wpv-loop-start -->', '', $out);
		$out = str_replace('<!-- wpv-loop-end -->', '', $out);
		return $out;
	}

	/**
	 * Removes "new line", "return" and "tab" characters from the shortcode output when the selected layout output is
	 * "List with separators" and when the user has chosen to remove the wrapping element of the View.
	 *
	 * This is a callback for the "wpv_filter_wpv_view_shortcode_output " filter.
	 *
	 * @param string   $out The shortcode output.
	 * @param int|null $id  The ID of the View.
	 *
	 * @return string  The final shortcode output.
	 *
	 * @since 2.6.4
	 */
	public function trim_empty_characters_from_shortcode_output( $out, $id ) {
		if (
			// Check if the View wrapper DIV (and the filter FORM along with the pagination) is required.
			//** This filter is documented in embedded/inc/wpv-layout-embedded.php */
			! apply_filters( 'wpv_filter_wpv_is_wrapper_div_required', true, $id ) &&
			/**
			 * wpv_filter_wpv_is_separators_list_layout_selected
			 *
			 * Checks if the list with separators is selected as the View layout.
			 *
			 * @param bool     $is_separators_list_layout_selected
			 * @param null|int $view_id                            The ID of the View to check.
			 *
			 * @since 2.6.4
			 */
			apply_filters( 'wpv_filter_wpv_is_separators_list_layout_selected', false, $id )
		) {
			$out = str_replace( array( "\n", "\r", "\t" ), '', $out );
		}

		return $out;
	}

    /**
     * Can we use cache for this View?
	 *
	 * Note that the different checks are properly ordered, since:
	 * - there are some checks that are mandatory.
	 * - there are some checks that can be bypassed by using a cached="force" attribute
	 *
     * @param int $view_id View ID
	 * @param array $view_attributes The attributes passed to the View or View form shortcode
     * @return boolean
	 * @since unknown
     */
    private function is_cacheable( $view_id, $view_attributes = array() ) {
		// ===============================
		// Mandatory checks.
		// ===============================

		// Disable for views written on the backend or via REST.
		if( is_admin()
			|| ( defined( 'REST_REQUEST' ) && REST_REQUEST )
			|| ( defined( 'WPV_CACHE' ) && ! WPV_CACHE )
			|| ( isset( $_GET['preview_id'] ) && isset( $_GET['preview_nonce'] ) )
		) {
			return false;
		}

		// If "cached" is set to "off", then the View output should be generated again.
	    if ( 'off' === toolset_getarr( $view_attributes, 'cached' ) ) {
            return false;
		}

		// If arbitrary attributes are passed to the View shortcode,
		// those should modify the outcome by a query filter.
		$existing_attributes = array_keys( $view_attributes );
		$accepted_attributes = array( 'name', 'id', 'view_display', 'cached', 'target_id' );
		$modifyer_attributes = array_diff( $existing_attributes, $accepted_attributes );
		if ( ! empty( $modifyer_attributes ) ) {
			return false;
		}

		// Only the default first page can be cached,
		// and only when it is not modified by core URL parameters
		// or by URL parameters that the Viw is listening to.
		$wpv_core_url_parameter_modifiers = array(
			// Table sorting: do not cache if manually modified column orderby or order.
			'wpv_column_sort_id', 'wpv_column_sort_dir',
			// Frontend sorting controls: do not cache if orderby or order are enforced.
			'wpv_sort_orderby', 'wpv_sort_order', 'wpv_sort_orderby_as', 'wpv_sort_orderby_second', 'wpv_sort_order_second',
			// Paged and frontend search parameters.
			'wpv_paged', 'wpv_view_count', 'wpv_post_search', 'wpv_taxonomy_search'
		);
		foreach ( $wpv_core_url_parameter_modifiers as $modifier ) {
		    if ( isset( $_GET[ $modifier ] ) ) {
				return false;
		    }
		}
		$url_parameters_to_listen = get_view_allowed_url_parameters( $view_id );
	    foreach ( $url_parameters_to_listen as $param ) {
		    if ( isset( $_GET[ $param['attribute'] ] ) ) {
				return false;
		    }
		}

		// Cache cannot be used while the user is debugging Views.
		global $WPV_settings, $WPVDebug;
        $is_debug_mode_on = isset( $WPV_settings->wpv_debug_mode ) && ! empty( $WPV_settings->wpv_debug_mode );
        $current_user_can_debug = $WPVDebug->user_can_debug();
        if ( $is_debug_mode_on && $current_user_can_debug ) {
            return false;
		}

		$view_settings = $this->get_view_settings( $view_id );

		/**
		 * Disable caching if certains conditions are met.
		 *
		 * @param bool
		 * @param int $view_id
		 * @param array $view_settings
		 * @param array $view_attributes
		 * @return bool
		 * @since unknown
		 */
        $requirement_result = apply_filters( 'wpv_filter_disable_caching', false, $view_id, $view_settings, $view_attributes );
		// Disable caching by a filter.
		if ( $requirement_result ) {
            return false;
		}

		$view_layout_settings = $this->get_view_layout_settings( $view_id );
		$view_layout_html = toolset_getarr( $view_layout_settings, 'layout_meta_html' );

		/**
		 * List of shortcodes that, when placed inside a View layout, should disable the caching mechanism.
		 *
		 * - Playlists require a template to be loaded, hence they disable the cache.
		 * - Maps also registers its own shortcode here.
		 * - Forms should also register its own shortcodes, at least whilenform submission is managed on form printing.
		 *
		 * @param array
		 * @return array
		 * @since 2.9.4
		 */
		$shortcodes_to_shortcircuit_cache = apply_filters(
			'wpv_filter_shortcodes_should_disable_view_cache',
			array(
				'playlist',
			)
		);
		// Nested Views or Content Templates, or playlists should disable cache.
		foreach ( $shortcodes_to_shortcircuit_cache as $shortcode_candidate ) {
			if ( false !== strpos( $view_layout_html, '[' . $shortcode_candidate ) ) {
				return false;
			}
		}

		// ===============================
		// Bypass with a shortcode attribute.
		// ===============================

		if ( 'force' === toolset_getarr( $view_attributes, 'cached' ) ) {
            return true;
		}

		// ===============================
		// Opional checks.
		// It is up to the user to invalidate cache when conditions change.
		// ===============================

		// If rendering just the form targeting other page, do not cache.
		// Optional because the View form can be used in a single place of the site,
		// hence caching is allowed because the form target does not change.
        if ( 'self' !== toolset_getarr( $view_attributes, 'target_id', 'self' ) ) {
            return false;
        }

		// The View settings might be filtered to disable caching.
		// Optional because an attribute to force cache has higher priority.
		if ( true === toolset_getarr( $view_settings, 'disable_caching' ) ) {
            return false;
        }

		// Randomly sorted Views can not be cached.
		// Optional because an attribute to force cache has higher priority.
        if ( 'rand' === toolset_getarr( $view_settings, 'orderby' )	) {
            return false;
        }
        if ( 'rand' === toolset_getarr( $view_settings, 'orderby_second' )	) {
            return false;
        }

		// Environment depending View can not be cached.
		// Optional because an attribute to force cache has higher priority:
		// if the View is used in a single place, those environmental restrictions are kept for the cached version.
		$view_settings_no_override = apply_filters( 'wpv_filter_wpv_get_view_settings', array(), $view_id, array( 'override_view_settings' => false )  );
        $requirement_list = array(
            'wpv_filter_requires_current_page',
            'wpv_filter_requires_current_archive',
            'wpv_filter_requires_current_user',
            'wpv_filter_requires_parent_user',
            'wpv_filter_requires_parent_term',
			'wpv_filter_requires_parent_post',
			'wpv_filter_requires_framework_values',
        );
        foreach ( $requirement_list as $requirement ) {
            $requirement_result = apply_filters( $requirement, false, $view_settings_no_override );
            if ( $requirement_result ) {
                return false;
            }
        }

		$shortcodes_to_maybe_shortcircuit_cache = array(
			'wpv-view',
			'wpv-post-body',
		);
		// Nested Views or Content Templates should disable cache,
		// mostly because we do not known anything about who triggers changes in them,
		// ans algo because they might include extra CSS and JS that needs printing.
		// Optional because an attribute to force cache has higher priority.
		foreach ( $shortcodes_to_maybe_shortcircuit_cache as $shortcode_candidate ) {
			if ( false !== strpos( $view_layout_html, '[' . $shortcode_candidate ) ) {
				return false;
			}
		}



		// Nested Views or Content Templates, or playlists should disable cache.
		if ( function_exists( 'has_blocks' ) ) {
			/**
			 * List of blocks that, when placed inside a View, should disable the caching mechanism.
			 *
			 * - Conditional block might depend on a condition that changed after the cache was created, hence it disables the cache.
			 *
			 * @param array
			 *
			 * @return array
			 *
			 * @since 3.2
			 */
			$blocks_to_shortcircuit_cache = apply_filters(
				'wpv_filter_blocks_should_disable_view_cache',
				array(
					'toolset-blocks/conditional',
				)
			);
			foreach ( $blocks_to_shortcircuit_cache as $block_candidate ) {
				if ( has_block( $block_candidate, $view_layout_html ) ) {
					return false;
				}
			}
		}

        return true;
    }

	function wpv_is_rendering_form_view( $false ) {
		return $this->rendering_views_form();
	}

	function rendering_views_form() {
		return $this->rendering_views_form_in_progress;
	}


	function get_current_page() {
		$aux_array = $this->current_page;
		return end( $aux_array );
	}

	function wpv_get_current_post( $current_post = null ) {
		$maybe_current_post = $this->get_current_page();
		if ( $maybe_current_post ) {
			$current_post = $maybe_current_post;
		}
		return $current_post;
	}

	function wpv_set_current_post( $current_post ) {
		$post_exists = ( isset( $current_post ) && $current_post instanceof WP_Post );
		if (
			isset( $current_post )
			&& $current_post instanceof WP_Post
		) {

		}
		array_push( $this->current_page, clone $current_post );
	}

	function wpv_get_view_shortcodes_attributes( $attributes = false ) {
		$attributes = $this->get_view_shortcodes_attributes();
		if ( ! is_array( $attributes ) ) {
			$attributes = array();
		}
		return $attributes;
	}

	function get_view_shortcodes_attributes() {
		$aux_array = $this->view_shortcode_attributes;
		$current_view_shortcode_atribues = end( $aux_array );
		return is_array( $current_view_shortcode_atribues )
			? $current_view_shortcode_atribues
			: array();
	}

	/**
	* set_view_shortcode_attributes
	*
	* Sets the shortcode attributess for the current View.
	*
	* Note that calling this twice on the same View will have undesired effects. Use with caution.
	* - Shortcode attributes are pushed into an array when a View is about to be rendered.
	* - Shortcode attributes are poped from that array after the View has been rendered.
	* - This way, several nested Views can have their attributes stored and in sync with the currently rendered one.
	* - Pushing twice for the same View mwans that only the last set is used, and the sync for nested Views is broken.
	*
	* This method is useful when doing AJAX cals, where you know that no View is being loaded, but you need to fake the View shortcode attributes to calculate its hash.
	*/

	function set_view_shortcode_attributes( $attributes ) {
		array_push( $this->view_shortcode_attributes, $attributes );
	}

	/**
	 * Helper method/API to reset the shortcode attributes for current View,
	 * for API functions that need to get View results based on their ID.
	 *
	 * @since 2.3.0
	 */
	function reset_view_shortcode_attributes() {
		array_pop( $this->view_shortcode_attributes );
	}


	function get_top_current_page() {
		if ( isset( $_GET['wpv_aux_current_post_id'] ) ) {
			// In AJAX pagination is_single() and is_page() do not work as expected, but it seems they return TRUE here anyway
			$top_post_id = esc_attr( $_GET['wpv_aux_current_post_id'] );
			$top_post = get_post( $top_post_id );
			$this->top_current_page = $top_post;
			return $this->top_current_page;
		} else if ( is_single() || is_page() ) {
			// In this case, check directly the current page - needed to make the post_type_dont_include_current_page setting work in AJAX pagination
			global $wp_query;
			if ( isset( $wp_query->posts[0] ) ) {
				$current_post = $wp_query->posts[0];
				return $current_post;
			} else {
				return $this->top_current_page;
			}
		} else {
			return $this->top_current_page;
		}
	}

	function wpv_get_top_current_post( $top_current_post = null ) {
		$top_current_post = $this->get_top_current_page();
		return $top_current_post;
	}

	function wpv_set_top_current_post( $top_current_post ) {
		$this->top_current_page = $top_current_post;
	}

	function wpv_get_current_view( $current_view = null ) {
		$current_view = $this->get_current_view();
		return $current_view;
	}

	/**
	 * Get the tree of currently nested Views displayed on the page.
	 *
	 * Returns an array of currently looped Views, in order of appearance,
	 * or an empty array otherwise.
	 *
	 * @dince 2.3.0
	 */

	function wpv_get_current_views_tree( $current_views_tree = array() ) {
		$current_views_tree = $this->view_ids;
		return $current_views_tree;
	}

	/**
	* Get the current view we are processing.
	*/
	function get_current_view() {
		return $this->current_view;
	}

	/**
	 * Helper method/API to set the current View, for API functions that need to get View results based on their ID.
	 *
	 * @param $view_id integer The View ID.
	 *
	 * @since 2.3.0
	 */
	function wpv_set_current_view( $view_id ) {
		array_push( $this->view_ids, $this->current_view );
		$this->current_view = $view_id;
	}

	/**
	 * Helper method/API to reset the current View, for API functions that need to get View results based on their ID.
	 *
	 * @param $view_id_fallback integer The View ID to set as fallback, for backwards consistency.
	 *
	 * @since 2.3.0
	 */
	function wpv_reset_current_view( $view_id_fallback = null ) {
		$this->current_view = array_pop( $this->view_ids );
		if ( $this->current_view == null ) {
			$this->current_view = $view_id_fallback;
		}
	}

	function wpv_get_object_unique_hash( $hash = '', $view_settings = array() ) {
		$view_settings['view-query-mode'] = isset( $view_settings['view-query-mode'] ) ? $view_settings['view-query-mode']  : 'normal';
		switch ( $view_settings['view-query-mode'] ) {
			case 'archive':
			case 'layouts-loop':
				$hash = apply_filters( 'wpv_filter_wpv_get_archive_unique_hash', '' );
				break;
			case 'normal':
			default:
				$hash = apply_filters( 'wpv_filter_wpv_get_view_unique_hash', '' );
				break;
		}
		return $hash;
	}


	function wpv_get_view_unique_hash( $view_unique_hash = '' ) {
		$view_unique_hash = $this->get_view_count();
		return $view_unique_hash;
	}

	/**
	* Get the current view count.
	*/
	function get_view_count() {
		$attr_attr = '';
		$attr = $this->get_view_shortcodes_attributes();
		$ignore = array(
			'name',
			'id',
			'target_id',
			'view_display',
			'limit',
			'offset',
			'orderby',
			'order',
			'orderby_as',
			'orderby_second',
			'order_second',
			'cached'
		);
		foreach ( $ignore as $ig_key ) {
			if ( isset( $attr[ $ig_key ] ) ) {
				unset( $attr[ $ig_key ] );
			}
		}
		if ( ! empty( $attr ) ) {
			// Let's assume that shortcode attributes must be strings
			$attr = array_map( 'strval', $attr );
			ksort( $attr );
			$attr_attr = 'CATTR' . md5( serialize( $attr ) );
		}

		$view_settings = $this->get_view_settings();
		$attr_top_current_post = '';
		$requires_top_current_post = false;
		/**
		* wpv_filter_requires_current_page
		*
		* Whether the current View requires the current page for any filter
		*
		* @param $requires_top_current_post boolean
		* @param $view_settings
		*
		* @since unknown
		*/
        $requires_top_current_post = apply_filters('wpv_filter_requires_current_page', $requires_top_current_post, $view_settings);
			if ( $requires_top_current_post ) {
			$top_current_post = $this->get_top_current_page();
			if (
				$top_current_post
				&& isset( $top_current_post->ID )
			) {
				$attr_top_current_post = 'TCPID' . intval( $top_current_post->ID );
			}
		}
		$attr_current_post = '';
		$requires_current_post = false;
		/**
		* wpv_filter_requires_parent_post
		*
		* Whether the current View is nested and requires the parent post for any filter
		*
		* @param $requires_current_post boolean
		* @param $view_settings
		*
		* @since unknown
		*/
		$requires_current_post = apply_filters( 'wpv_filter_requires_parent_post', $requires_current_post, $view_settings );
		if ( $requires_current_post ) {
			$current_post = $this->get_current_page();
			if (
				$current_post
				&& isset( $current_post->ID )
			) {
				$attr_current_post = 'CPID' . intval( $current_post->ID );
			}
		}
		$attr_term = '';
		$requires_parent_term = false;
		/**
		* wpv_filter_requires_parent_term
		*
		* Whether the current View is nested and requires the parent term for any filter
		*
		* @param $requires_parent_term boolean
		* @param $view_settings
		*
		* @since unknown
		*/
		$requires_parent_term = apply_filters( 'wpv_filter_requires_parent_term', $requires_parent_term, $view_settings );
			if ( $requires_parent_term ) {
			if ( $this->get_parent_view_taxonomy() ) {
				$attr_term = 'CTID' . intval( $this->get_parent_view_taxonomy() );
			}
		}
		$attr_user = '';
		$requires_parent_user = false;
		/**
		* wpv_filter_requires_parent_user
		*
		* Whether the current View is nested and requires the parent user for any filter
		*
		* @param $requires_parent_user boolean
		* @param $view_settings
		*
		* @since unknown
		*/
		$requires_parent_user = apply_filters( 'wpv_filter_requires_parent_user', $requires_parent_user, $view_settings );
		if ( $requires_parent_user ) {
			if ( $this->get_parent_view_user() ) {
				$attr_user = 'CUID' . intval( $this->get_parent_view_user() );
			}
		}

		$attr_suffix = $attr_attr . $attr_top_current_post . $attr_current_post . $attr_term . $attr_user;
		if ( ! empty( $attr_suffix ) ) {
			$attr_suffix = '-' . $attr_suffix;
		}

		$return = $this->current_view . $attr_suffix;

		return (string) $return;
	}


	function set_view_count( $count, $view_id ) {
		if ( $view_id ) {
			$this->set_view_counts[ $view_id ] = $count;
		} else {
			$this->view_count[ $this->view_depth ] = $count;
		}
	}

	function wpv_get_view_settings( $view_settings = array(), $view_id = null, $options_array = array( 'override_view_settings' => true, 'extend_view_settings' => true, 'public_view_settings' => true, 'original_view_settings' => null ) ) {
		$view_settings = $this->get_view_settings( $view_id, null, $options_array );
		return $view_settings;
	}

	/**
	 * Get the view settings for a given or the current View.
	 *
	 * @param integer $view_id View post ID.
	 * @param array|null $post_meta If not null, this value will be used instead of querying the '_wpv_settings'
	 *	 postmeta of given View. Please refer to wpv_prepare_view_listing_query() to understand why it is necessary
	 *	- usually because we already got the _wpv_settings postmeta for the View and just want to normalize  and filter the output
	 * @param array $options_array (array) Unserialized array with options
     *
	 * @return array View's settings.
	 *
	 * @since unknown
	 * @singe 2.3.0 Added the $options_array argument to extend the arguments list of the filter. It contains the 'override_view_settings'
	 * boolean (former '$disable_override' boolean argument), the 'extend_view_settings' boolean which decides if the resulting $view_settings
	 * array will be extended and the 'original_view_settings' array which contains the original metadata before the filters are applied.
	 */

	function get_view_settings( $view_id = null, $post_meta = null, $options_array = array( 'override_view_settings' => true, 'extend_view_settings' => true, 'public_view_settings' => true, 'original_view_settings' => null ) ) {
		if ( is_null( $view_id ) ) {
			$view_id = $this->get_current_view();
		}
		// Normalize _wpv_settings postmeta if we got that earlier
		if ( null == $post_meta ) {
			$post_meta = (array) get_post_meta( $view_id, '_wpv_settings', true );
			$options_array[ 'original_view_settings' ] = $post_meta;
		}

        $options_array_defaults = array(
            'override_view_settings' => true,
            'extend_view_settings' => true,
            'public_view_settings' => true,
            'original_view_settings' => null
        );
        $options_array = wp_parse_args( $options_array, $options_array_defaults );

		/**
		* wpv_view_settings
		*
		* Internal filter to set some View settings that will overwrite the ones existing in the _wpv_settings postmeta
		* Only used to set default values that need to be there on the returned array, but may not be there for legacy reasons
		* Use wpv_filter_override_view_settings to override View settings - like on the Theme Frameworks integration
		*
		* @param $post_meta (array) Unserialized array of the _wpv_settings postmeta
		* @param $view_id (integer) The View ID
		* @param $options_array (array) Unserialized array with options
		*
		* @return $view_settings (array) The View settings
		*
		* @since unknown
		* @singe 2.3.0 Added the $options_array argument to extend the arguments list of the filter. It contains the 'override_view_settings'
		* boolean (former '$disable_override' boolean argument), the 'extend_view_settings' boolean which decides if the resulting $view_settings
		* array will be extended and the 'original_view_settings' array which contains the original metadata before the filters are applied.
		*/
		$view_settings = apply_filters( 'wpv_view_settings', $post_meta, $view_id, $options_array );

		// @todo Move this functionality to a better place :-)
		if( $options_array['extend_view_settings'] ) {

			$current_view = get_post( $view_id );

			$view_settings['view_id'] = $view_id;
			$view_settings['view_slug'] = isset( $current_view ) ? $current_view->post_name : "";
			$view_settings['view_description'] = get_post_meta( $view_id, '_wpv_description', true );
		}

        if( $options_array['override_view_settings'] ) {

			/**
			 * wpv_filter_override_view_settings
			 *
			 * Public filter to set some View settings that will overwrite the ones existing in the _wpv_settings postmeta
			 * For example, on the Theme Frameworks integration
			 *
			 * @param $view_settings (array) The View settings
			 * @param $view_id (integer) The View ID
			 * @param $options_array (array) Unserialized array with options
			 *
			 * @return $view_settings (array) The View settings
			 *
			 * @since 1.8.0
			 * @singe 2.3.0 Added the $options_array argument to extend the arguments list of the filter. It contains the 'override_view_settings'
			 * boolean (former '$disable_override' boolean argument), the 'extend_view_settings' boolean which decides if the resulting $view_settings
			 * array will be extended and the 'original_view_settings' array which contains the original metadata before the filters are applied.
			 */
            $view_settings = apply_filters( 'wpv_filter_override_view_settings', $view_settings, $view_id, $options_array );
        }

		if( $options_array['public_view_settings'] ) {

			/**
			 * wpv_filter_public_wpv_view_settings
			 *
			 * Public filter to set some View settings that will overwrite the ones existing in the _wpv_settings postmeta
			 *
			 * @param $view_settings (array) The View settings
			 * @param $view_id (integer) The View ID
			 * @param $options_array (array) Unserialized array with options
			 *
			 * @return $view_settings (array) The View settings
			 *
			 * @singe 2.3.0
			 */
			$view_settings = apply_filters( 'wpv_filter_public_wpv_view_settings', $view_settings, $view_id, $options_array );
		}

		return $view_settings;
	}


	/**
	 * Callback hooked into the wpv_view_settings filter to set default values
	 * that should be in the _wpv_settings postmeta but might be missing somehow
	 *
	 * @param $view_settings (array)
	 * @param $view_id (integer)
	 * @return array $view_settings (array)
	 *
	 * @since 1.8.0
	 */
	function wpv_view_settings_set_fallbacks( $view_settings, $view_id ) {
		if ( ! is_array( $view_settings ) ) {
			$view_settings = array();
		}
		// Query mode
		if ( ! isset( $view_settings['view-query-mode'] ) ) {
			$view_settings['view-query-mode'] = 'normal';
		}
		return $view_settings;
	}

	function wpv_get_view_layout_settings( $view_layout_settings = array(), $view_id = null ) {
		$view_layout_settings = $this->get_view_layout_settings( $view_id );
		return $view_layout_settings;
	}

	/**
	 * Get the view layout settings for a given or the current View.
	 *
	 * @param integer $view_id View post ID.
	 * @param array|null $post_meta If not null, this value will be used instead of querying the '_wpv_layout_settings'
	 *	 postmeta of given View. Please refer to wpv_prepare_view_listing_query() to understand why it is necessary
	 *	- usually because we already got the _wpv_layout_settings postmeta for the View and just want to normalize  and filter the output
	 *
	 * @return array View's settings.
	 *
	 * @since unknown
	 */

	function get_view_layout_settings( $view_id = null, $post_meta = null ) {
		if ( is_null( $view_id ) ) {
			$view_id = $this->get_current_view();
		}
		// Normalize _wpv_layout_settings postmeta if we got that earlier
		if ( null == $post_meta ) {
			$post_meta = (array) get_post_meta( $view_id, '_wpv_layout_settings', true );
		}

		/**
		* wpv_view_layout_settings
		*
		* Internal filter to set some View layout settings that will overwrite the ones existing in the _wpv_layout_settings postmeta
		* Only used to set default values that need to be there on the returned array, but may not be there for legacy reasons
		* Use wpv_filter_override_view_layout_settings to override View layout settings
		*
		* @param $post_meta (array) Unserialized array of the _wpv_layout_settings postmeta
		* @param $view_id (integer) The View ID
		*
		* @return $view_layout_settings (array) The View layout settings
		*
		* @since 1.8.0
		*/

		$view_layout_settings = apply_filters( 'wpv_view_layout_settings', $post_meta, $view_id );

		/**
		* wpv_filter_override_view_layout_settings
		*
		* Public filter to set some View layout settings that will overwrite the ones existing in the _wpv_layout_settings postmeta
		*
		* @param $view_layout_settings (array) The View layout settings
		* @param $view_id (integer) The View ID
		*
		* @return $view_layout_settings (array) The View layout settings
		*
		* @since 1.8.0
		*/

		$view_layout_settings = apply_filters( 'wpv_filter_override_view_layout_settings', $view_layout_settings, $view_id );

		return $view_layout_settings;
	}

	/**
	* wpv_view_layout_settings_set_fallbacks
	*
	* Callback hooked into the wpv_view_settings filter to set default values
	* that should be in the _wpv_settings postmeta but might be missing somehow
	*
	* @param $view_settings (array)
	* @param $view_id (integer)
	*
	* @return $view_settings (array)
	*
	* @since 1.8.1
	*/

	function wpv_view_layout_settings_set_fallbacks( $view_layout_settings, $view_id ) {
		if ( ! is_array( $view_layout_settings ) ) {
			$view_layout_settings = array();
		}
		return $view_layout_settings;
	}

	/**
	* clean_current_loop_user
	*
	* Clean the global data for the current user on a loop for a View listing users right after rendering it.
	*
	* This is useful and needed to avoid data leaking caused for persistance of this global values, related to the last rendered user.
	* Without this, the wpv-user shortcode used on a View listing users but ourside the loop, or after the View has been rendered
	* will reurn values related to the last rendered user, instead to the current user as default.
	*
	* @since 1.10
	*/

	function clean_current_loop_user() {
		$this->users_data['term'] = null;
	}

	/**
	 * Keep track of the current view and render the view.
	 */
	function render_view_ex( $id, $hash ){

		global $post, $WPVDebug;

		$this->view_depth++;
		$WPVDebug->wpv_debug_start( $id, $this->view_shortcode_attributes );

        $post_exists = ( isset( $post ) && $post instanceof WP_Post );

		if ( $this->top_current_page == null ) {
			$this->top_current_page = ( $post_exists ? clone $post : null );
		}

		array_push( $this->current_page, $post_exists ? clone $post : null );

		array_push( $this->view_ids, $this->current_view );

		// Adjust for WPML support
		// Although Views are not translatable anymore, keep for backwards compatibility
		$id = apply_filters( 'translate_object_id', $id, 'view', true, null );

		$this->current_view = $id;

		array_push( $this->post_query_stack, $this->post_query );

		// save original taxonomy term if any
		$tmp_parent_taxonomy = $this->parent_taxonomy;
		if ( isset( $this->taxonomy_data['term'] ) ) {
			$this->parent_taxonomy = $this->taxonomy_data['term']->term_id;
		} else {
			if (
				$this->parent_taxonomy
				&& isset( $_GET['wpv_aux_parent_term_id'] )
				&& is_numeric( $_GET['wpv_aux_parent_term_id'] )
				&& $_GET['wpv_aux_parent_term_id'] == $this->parent_taxonomy
			) {
				$this->parent_taxonomy = intval( $_GET['wpv_aux_parent_term_id'] );
			} else {
				$this->parent_taxonomy = 0;
			}
		}
		$tmp_taxonomy_data = $this->taxonomy_data;

		// save original users if any
		$tmp_parent_user = $this->parent_user;
		if ( isset( $this->users_data['term'] ) ) {
			$this->parent_user = $this->users_data['term']->ID;
		} else {
			if (
				$this->parent_user
				&& isset( $_GET['wpv_aux_parent_user_id'] )
				&& is_numeric( $_GET['wpv_aux_parent_user_id'] )
				&& $_GET['wpv_aux_parent_user_id'] == $this->parent_user
			) {
				$this->parent_user = intval( $_GET['wpv_aux_parent_user_id'] );
			} else {
				$this->parent_user = 0;
			}
		}
		$tmp_users_data = $this->users_data;

		$out =  $this->render_view( $id, $hash );

		if (
			$post_exists
			&& $this->is_archive_view( $id )
		) {
			/**
			* On WPAs, the global $post is valid inside the <wpv-loop></wpv-loop> loop, since each post sets its global,
			* but outside that loop, the global $post was defaulting to the first post in the global $wp_query.
			*
			* It caused that Views used outside the loop with "Don't include current page in query result" turned on
			* were not including the first result, when they should.
			*
			* So we need to temporarily unset the global $post when expanding shortcodes outside the loop on WPA.
			* To avoid problems with date-based archive pages and the [wpv-archive-title] shortcode, we must keep the object and its date,
			* hence we just modify the global $post properties to use the WPA ones instead.
			*
			* @since 1.10
			* @updated 1.11
			* @since 2.0		Do not use 'view' as post_type since Views rendered outside the wpv-loop and using a CT loop will fail to render it:
			* 					The wpv-post-body shortcode stops rendering when the curent post has a post_type of 'view' or 'view-template'
			* 					Let's use a dummy value here, as this will only affect this little piece and we should be more or less covered.
			*/

			$registered_post_types = get_post_types( array(), 'names' );
			$dummy_post_type_counter = 0;
			$dummy_post_type_base = 'view-dummy';
			$dummy_post_type = 'view-dummy';

			while ( in_array( $dummy_post_type, $registered_post_types ) ) {
				$dummy_post_type_counter = $dummy_post_type_counter + 1;
				$dummy_post_type = $dummy_post_type_base . '-' . $dummy_post_type_counter;
			}

			// A clone of the global post instead of a reference assignment...
			$temp_post = clone $post;
			$post->ID = $id;
			$post->post_type = $dummy_post_type;
			$post->post_parent = 0;
		}


		$out = wpv_do_shortcode( $out );

		if (
			$post_exists
			&& $this->is_archive_view( $id )
		) {
			/**
			* Restore back the current global $post.
			*
			* Not sure this is needed at all, but better keep it just in case.
			*
			* @since 1.10
			* @since 3.0  Instead of reverting the whole post, only the modified value are reverted as this would create
			*             for methods setting the global post and never reassigning this to their local variables.
			*/
			$post->ID = $temp_post->ID;
			$post->post_type = $temp_post->post_type;
			$post->post_parent = $temp_post->post_parent;
			$temp_post = null;
		}

		$this->taxonomy_data = $tmp_taxonomy_data;
		$this->parent_taxonomy = $tmp_parent_taxonomy;

		$this->users_data = $tmp_users_data;
		$this->parent_user = $tmp_parent_user;

		$this->current_view = array_pop( $this->view_ids );

		array_pop( $this->current_page );

		$this->post_query = array_pop( $this->post_query_stack );

		$this->view_depth--;
		$WPVDebug->wpv_debug_end();

		if (
			0 === $this->view_depth
			&& ! is_singular()
		) {
			// Permanently store the current top page when on a singular one, optionally remove it elsewhere.
			// Note that we only manipulate this on top.level Views.
			// Otherwise, this notion makes no sense and can affect other Views in the same archive page,
			// or even Views processed by third parties in a batch.
			$this->top_current_page = null;
		}

		/**
		 * Filters the output of a View/WordPress Archive.
		 *
		 * @param string $out The output markup of the View.
		 * @param int    $id  The ID of the View.
		 */
		return apply_filters( 'wpv_filter_view_output', $out, $id );
	}

	/**
	 * For frontend styles it's important that the 'render_block' filter is running. But WPA does only store
	 * the block meta of the WPA content, not the loop / search / pagination / ... which can also have stylings.
	 * Fetching the helper post, which contains the original data and running do_blocks on it.
	 *
	 * @param WP_Post $view
	 */
	private function do_blocks_to_trigger_render_block_filter( WP_Post $view ) {
		if( ! function_exists( 'do_blocks' ) ) {
			// Old WP. No need for this.
			return;
		}

		$args = array(
			'post_parent' => $view->ID,
			'post_type' => \OTGS\Toolset\Views\Controller\Compatibility\BlockEditorWPA::WPA_HELPER_POST_TYPE,
		);

		$wpa_helper = get_posts( $args );

		foreach ( $wpa_helper as $wpa_helper_post ) {
			do_blocks( $wpa_helper_post->post_content );
		}
	}

	/**
	 * Render the view and loops through the found posts
	 */
	function render_view( $view_id, $hash ){

		global $post, $WPVDebug;

		static $processed_views = array();

		// Reset the forced disabling of dependant parametric search: this depends on each View, should not be global.
		$this->check_force_disable_dependant_parametric_search();

		// increment the view count.
		// TODO this code is duplicated, maybe create function for it?
		if ( !isset( $this->view_count[ $this->view_depth ] ) ) {
			$this->view_count[ $this->view_depth ] = 0;
		}
		$this->view_count[ $this->view_depth ]++;

		$view = get_post( $view_id );
		$this->view_used_ids[] = $view_id;

		do_action( 'wpv_action_require_frontend_assets' );

		$out = '';

		$view_caller_id = ( isset( $post ) && isset( $post->ID ) ) ? get_the_ID() : 0; // post or widget

		if( !isset( $processed_views[ $view_caller_id ][ $hash ] ) || 0 === $view_caller_id ) {
			//$processed_views[$view_caller_id][$hash] = true; // mark view as processed for this post

			$status = get_post_status( $view_id );

            // Views should be 'publish'ed to be allowed to produce an output
            // FIXME: Check also that user has permissions to render this view
			if( !empty( $view ) && ( $status == 'publish' || $status == 'draft' ) ) {

				/**
				 * This filter is used to apply translations to the view.
				 *
				 * @since 1.3
				 */
				$post_content = apply_filters( 'wpv_post_content', $view->post_content, $view->ID );

				// apply the layout meta html if we have some.
				$view_layout_settings = $this->get_view_layout_settings();

				if ( isset( $view_layout_settings['layout_meta_html'] ) ) {
					// TODO start remove (until "end remove") when views-3260 is implemented.
					$view_template_with_block_data = get_post_meta( $view_id, '_wpv_view_data', true );
					$view_template_with_block_data = is_array( $view_template_with_block_data ) &&
													 ! empty( $view_template_with_block_data['general']['view_template'] )
						? $view_template_with_block_data['general']['view_template']
						: false;

					if( $view_template_with_block_data ) {
						if( preg_match( '#<!-- wp:toolset-views/view-template-block(.*?)-->(.*?)<!-- /wp:toolset-views/view-template-block -->#s', $view_template_with_block_data, $match ) ) {
							include_once( __DIR__ . '/DomDocumentInnerHtml.php' );

							// Most servers have this by default, but save is save.
							$is_mb_running = function_exists( 'mb_detect_encoding' ) &&
										     function_exists( 'mb_internal_encoding' ) &&
										     function_exists( 'mb_convert_encoding' );

							// Take UTF-8 as default.
							$site_encoding = 'UTF-8';

							$utf8_layout_meta_html = $view_layout_settings['layout_meta_html'];

							if( $is_mb_running ) {
								// Get site encoding by the string of the database.
								$site_encoding = mb_detect_encoding( $view_layout_settings['layout_meta_html'] );
								// If encoding can not be determined use the internal encoding.
								$site_encoding = $site_encoding ?: mb_internal_encoding();

								// Load HTML with encoded HTML entities.
								$utf8_layout_meta_html = mb_convert_encoding(
									$utf8_layout_meta_html,
									'HTML-ENTITIES',
									$site_encoding
								);
							}

							$is_site_using_utf8 = strtolower( $site_encoding ) === 'utf-8';

							if( ! $is_site_using_utf8 ) {
								$utf8_layout_meta_html = utf8_encode( $utf8_layout_meta_html );
							}

							$use_internal_xml_errors = libxml_use_internal_errors( true );
							$dom_without_blocks_data = new DOMDocument();
							$dom_without_blocks_data->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
							$dom_without_blocks_data->loadHTML( $utf8_layout_meta_html );
							// We use a pseudo-tag wpv-loop which is called as invalid by DOMDocument::loadHTML.
							// Hence we need to catch the generated warning, and clean it.
							// Use libxml_get_errors() : mixed[] to verify that no extra errors are retrieved here.
							libxml_clear_errors();
							libxml_use_internal_errors( $use_internal_xml_errors );
							$xpath_without_blocks_data = new DOMXPath( $dom_without_blocks_data );

							// Also run the layout meta html through the DOM parser, because it alternates the HTML,
							// e.g. <p></p> becomes <p/> and 1:1 same content is required for replacement.
							$use_internal_xml_errors = libxml_use_internal_errors( true );
							$dom_layout_meta_html = new DOMDocument();
							$dom_layout_meta_html->registerNodeClass('DOMElement', 'JSLikeHTMLElement');
							$layout_meta_html_surrounded = '<div id="layout-meta-html">' . $utf8_layout_meta_html . '</div>';
							// Load HTML with encoded HTML entities.
							$dom_layout_meta_html->loadHTML( $layout_meta_html_surrounded );
							// We use a pseudo-tag wpv-loop which is called as invalid by DOMDocument::loadHTML.
							// Hence we need to catch the generated warning, and clean it.
							// Use libxml_get_errors() : mixed[] to verify that no extra errors are retrieved here.
							libxml_clear_errors();
							libxml_use_internal_errors( $use_internal_xml_errors );
							// Here we switched from the use of DOMDocument::getElementById to the use of DOMXPath and
							// then querying for element with ID using XPath to prevent issues with libxml library with version prior to 2.7.6.
							$dom_layout_meta_html_xpath = new DOMXPath( $dom_layout_meta_html );
							$layout_meta_html = $dom_layout_meta_html_xpath->query( "//*[@id='layout-meta-html']" )->item( 0 );

							foreach ($xpath_without_blocks_data->query('//div[contains(@class, "wpv-block-loop-item")]') as $div) {
								$html_loop_without_meta_translated = $div->parentNode->innerHTML;
								$html_complete_layout = $layout_meta_html->innerHTML;
								$html_loop_without_meta_not_translated = trim( $match[2] );

								// Loop with block meta. Just need to apply translation as that is not correctly
								// stored on the '_wpv_view_data' contains the same content for all translations.
								$html_loop_with_block_meta = str_replace(
									$html_loop_without_meta_not_translated,
									$html_loop_without_meta_translated,
									$match[0]
								);

								$view_layout_meta_html_with_blocks_data = str_replace(
									$html_loop_without_meta_translated,
									$html_loop_with_block_meta,
									$html_complete_layout
								);

								// DomDocument encodes shortcodes inside params. Undo it.
								// Use "rawurldecode" instead of "urldecode" as we want to maintain the "+" character mostly used in CSS styles.
								$view_layout_meta_html_with_blocks_data = rawurldecode(
									$view_layout_meta_html_with_blocks_data
								);

								// "urldecode" is known to mess with the value separator, "#+*#", used in the Dynamic Sources
								// shortcode as well as in the Dynamic Container shortcode. The value separator is transformed
								// into a similar string, "# *#", which breaks the parsing of the those shorcodes during the
								// View/WPA rendering (a good example is the Button block with DS for both the Text and the URL
								// inside a View). Thus the value separator needs to be converted back to its proper state.
								$view_layout_meta_html_with_blocks_data = str_replace( '# *#', '#+*#', $view_layout_meta_html_with_blocks_data );

								// At this stage the string is definetly utf-8.
								// Re-convert if site using different encoding.
								if( ! $is_site_using_utf8 && $is_mb_running ) {
									$view_layout_meta_html_with_blocks_data = mb_convert_encoding(
										$view_layout_meta_html_with_blocks_data,
										$site_encoding,
										'UTF-8'
									);
								}

								break;
							}
						}
					}
					// TODO end remove

					$view_layout_meta_html_with_blocks_data = isset( $view_layout_meta_html_with_blocks_data )
						? $view_layout_meta_html_with_blocks_data
						: $view_layout_settings['layout_meta_html'];

					/**
					 * Allows third-parties to filter the View Layout Meta HTML before the blocks will be converted to
					 * HTML, in order to extract information like block styles etc.
					 *
					 * @param string $view_layout_meta_html_with_blocks_data The View Layout Meta HTML.
					 * @param string $view_id                                The ID of the View.
					 */
					$view_layout_meta_html_with_blocks_data = apply_filters( 'wpv_view_pre_do_blocks_view_layout_meta_html', $view_layout_meta_html_with_blocks_data, $view_id );

					/**
					 * Triggers an action before doing the blocks in the View layout meta HTML.
					 *
					 * @param string $view_layout_meta_html_with_blocks_data
					 */
					do_action( 'wpv_action_before_doing_blocks_in_views_layout_meta_html', $view_layout_meta_html_with_blocks_data );

					// Use core function to render blocks.
					$layout_meta_html = function_exists( 'do_blocks' ) ?
						do_blocks( $view_layout_meta_html_with_blocks_data ) :
						$view_layout_meta_html_with_blocks_data;

					$post_content = str_replace('[wpv-layout-meta-html]', $layout_meta_html, $post_content );
				}

				$view_settings = $this->get_view_settings();

				if ( 'archive' === $view_settings['view-query-mode'] ) {
					// Register all blocks from the WPA.
					$this->do_blocks_to_trigger_render_block_filter( $view );
				}

				// find the loop
				if( preg_match( '#\<wpv-loop(.*?)\>(.*)</wpv-loop>#is', $post_content, $matches ) ) {
					// get the loop arguments.
					$args = $matches[1];
					$exp = array_map( 'trim', explode( ' ', $args ) );
					$args = array();
					foreach( $exp as $e ){
						$kv = explode( '=', $e );
						if ( sizeof( $kv ) == 2 ) {
							$args[ $kv[0] ] = trim( $kv[1] ,'\'"');
						}
					}
					if ( isset( $args[ 'wrap' ] ) ) {
						$args['wrap'] = intval( $args['wrap'] );
					}
					if ( isset( $args['pad'] ) ) {
						$args['pad'] = $args['pad'] == 'true';
					} else {
						$args['pad'] = false;
					}

					// Get templates for items (differentiated by their indices, see [wpv-item] documentation).
					$tmpl = $matches[2];
					$item_indexes = $this->_get_item_indexes( $tmpl );

					$query_type = apply_filters( 'wpv_filter_wpv_get_query_type', 'posts', $view_id );

					if ( $query_type == 'posts' ) {
						// get the posts using the query settings for this view.

						$archive_query = null;
						if ( $view_settings['view-query-mode'] == 'archive' ) {

							// check for an archive loop
							global $WPV_view_archive_loop;
							if ( isset( $WPV_view_archive_loop ) ) {
								$archive_query = $WPV_view_archive_loop->get_archive_loop_query();
							}

						} else if( $view_settings['view-query-mode'] == 'layouts-loop' ) {
							global $wp_query;
							$archive_query = ( isset( $wp_query ) && $wp_query instanceof WP_Query ) ? clone $wp_query : null;
						}

						if ( $archive_query ) {
							$this->post_query = $archive_query;
							$WPVDebug->add_log( 'mysql_query', $archive_query->request , 'posts', '', true );
							$WPVDebug->add_log( 'info', print_r( $archive_query, true ), 'query_results', '', true );
						} else {
							$this->post_query = wpv_filter_get_posts( $view_id );
						}
						$items = $this->post_query->posts;

						toolset_wplog( 'Found '. count( $items ) . ' posts', null, __FILE__, 'WP_Views::render_view', 1686 );

					}

					// save original post
					global $post, $authordata, $id;
					$tmp_post = ( isset( $post ) && $post instanceof WP_Post ) ? clone $post : null;
					$tmp_authordata = ( isset( $authordata ) && is_object( $authordata ) ) ? clone $authordata : null;
					$tmp_id = $id;

					if ( $query_type == 'taxonomy') {
						$items = $this->taxonomy_query( $view_settings );
						toolset_wplog( $items, 'debug', __FILE__, 'WP_Views::render_view', 1709 );
					} else if ( $query_type == 'users') {
						$items = $this->users_query( $view_settings );
						toolset_wplog( $items, 'debug', __FILE__, 'WP_Views::render_view', 1714 );
					}

                    $items_count = count( $items );

                    global $WPV_settings;
					if ( isset( $WPV_settings->wpv_debug_mode ) && !empty( $WPV_settings->wpv_debug_mode ) ) {
						$WPVDebug->add_log( 'items_count', $items_count );
					}

					// The actual loop - render all items
					$loop = '';

					/**
					 * Execute an action before the View loop.
					 *
					 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
					 * @param int $view_id    The ID of the View being looped
					 * @param array $args {
					 *     Loop arguments, if any, used when items are wrapped into rows.
					 *
					 *     @type int $wrap Optional. Number of items that each row sould include. If not set, all items will go into a single symbolic row.
					 *     @type bool $pad Optional. Whether the loop should include ghost items to complete a row, in case the items in a page do not cover the last row.
					 * }
					 * @since 2.7.3
					 */
					do_action( 'wpv_action_wpv_loop_before', $view_id, $query_type, $args );

					for( $i = 0; $i < $items_count; $i++) {
						$WPVDebug->set_index();
						$index = $i;

						if ( isset( $args['wrap'] ) ) {
							$index %= $args['wrap'];
						}

						// [wpv-item index=xx] uses base 1
						$index++;
						$index = strval( $index );

						/**
						 * Execute an action before each of the View loop items is being rendered.
						 *
						 * @param object $items[$i]  The object about to be displated, can be a WP_Post, a WP_Term, or a WP_User
						 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
						 * @param int    $view_id    The ID of the View being looped
						 *
						 * @since 2.4.0
						 */
						do_action( 'wpv_action_wpv_loop_before_display_item', $items[ $i ], $query_type, $view_id );

						switch ( $query_type ) {
							case 'posts':
								$post = clone $items[ $i ];
								$authordata = new WP_User( $post->post_author );
								$id = $post->ID;
								$temp_variables = $this->variables;
								$this->variables = array();
								do_action( 'wpv-before-display-post', $post, $view_id );
								break;
							case 'taxonomy':
								$this->taxonomy_data['term'] = $items[ $i ];
								do_action( 'wpv-before-display-taxonomy', $items[ $i ], $view_id );
								break;
							case 'users':
								$user_id = $items[ $i ]->ID;
								$user_meta = get_user_meta( $user_id );
								$items[ $i ]->meta = $user_meta;
								$this->users_data['term'] = $items[ $i ];
								do_action( 'wpv-before-display-user', $items[ $i ], $view_id );
								break;
						}

						$WPVDebug->add_log( $query_type , $items[ $i ] );

						// first output the "all" index.
						$shortcodes_output = wpv_do_shortcode( $item_indexes['all'] );

						/**
						 * Allow for 3rd party to modify the final shortcodes output.
						 *
						 * @param string $shortcodes_output The current output string
						 * @param int    $i                 The loop counter
						 * @param object $items[$i] The object about to be displayed, can be a WP_Post, a WP_Term, or a WP_User
						 * @param array  $view_settings     The view settings
						 *
						 * @since 2.9
						 */
						$shortcodes_output = apply_filters(
							'wpv_filter_view_loop_item_output',
							$shortcodes_output,
							$i,
							$items[ $i ],
							$view_settings
						);

						$loop .= $shortcodes_output;
						$WPVDebug->add_log_item( 'shortcodes', $item_indexes['all'] );
						$WPVDebug->add_log_item( 'output', $shortcodes_output );

						/* Select a template for this item based on it's index.
						 * Note: It is possible that we won't be rendering this item's content if the index 'other'
						 * isn't set and there is no other match. */
						$selected_index = null;
						if ( isset( $item_indexes[ $index ] ) ) {
							// First, set numeric templates
							$selected_index = $index;
						} elseif (
							(int) $index === $items_count &&
							isset( $item_indexes['last'] )
						) {
							// Then, set the template for the last element
							$selected_index = 'last';
						} else {
							// Else, set specific templates based on cases
							$index_data = array(
								'loop_index'	=> $i,
								'item_index'	=> $index,
								'avail_indexes'	=> $item_indexes,
								'view_id'		=> $view_id,
								'items_count'	=> $items_count
							);
							$selected_index = apply_filters( 'wpv_filter_wpv_item_loop_selected_index', $selected_index, $index_data );
						}

						// Finally there is an index 'other' and we did not set a valid template before, apply it
						if (
							null == $selected_index
							&& isset( $item_indexes['other'] )
						) {
							$selected_index = 'other';
						}

						// Output the item with appropriate template (if we found one)
						if( null !== $selected_index ) {
							$shortcodes_output = wpv_do_shortcode( $item_indexes[ $selected_index ] );
							$loop .= $shortcodes_output;
							$WPVDebug->add_log_item( 'shortcodes', $item_indexes[ $selected_index ] );
							$WPVDebug->add_log_item( 'output', $shortcodes_output );
						}

						/**
						 * Execute an action after each of the View loop items is rendered.
						 *
						 * @param object $items[$i]  The object just displated, can be a WP_Post, a WP_Term, or a WP_User
						 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
						 * @param int    $view_id    The ID of the View being looped
						 *
						 * @since 2.4.0
						 */
						do_action( 'wpv_action_wpv_loop_after_display_item', $items[ $i ], $query_type, $view_id );

						switch ( $query_type ) {
							case 'posts':
								do_action( 'wpv-after-display-post', $post, $view_id );
								$this->variables = $temp_variables;
								break;
							case 'taxonomy':
								do_action( 'wpv-after-display-taxonomy', $items[ $i ], $view_id );
								break;
							case 'users':
								do_action( 'wpv-after-display-user', $items[ $i ], $view_id );
								break;
						}

					}

					// see if we should pad the remaining items.
					if ( isset( $args['wrap'] ) && $args['pad'] ) {
						while ( $i % $args['wrap'] ) {
							$index = $i;
							$index %= $args['wrap'];
							if ( $index == $args['wrap'] - 1 ) {
								if ( isset( $item_indexes['pad-last'] ) ) {
									/**
									 * Execute an action before each of the View loop ghost pad indexes is rendered.
									 *
									 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
									 * @param int $view_id The ID of the View being looped
									 * @since 2.7.3
									 */
									do_action( 'wpv_action_wpv_loop_before_display_pad_item', $query_type, $view_id );
									/**
									 * Execute an action before each of the View loop ghost last pad index is rendered.
									 *
									 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
									 * @param int $view_id The ID of the View being looped
									 * @since 2.7.3
									 */
									do_action( 'wpv_action_wpv_loop_before_display_pad_last_item', $query_type, $view_id );
									$loop .= wpv_do_shortcode( $item_indexes['pad-last'] );
									/**
									 * Execute an action after each of the View loop ghost pad indexes is rendered.
									 *
									 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
									 * @param int $view_id The ID of the View being looped
									 * @since 2.7.3
									 */
									do_action( 'wpv_action_wpv_loop_after_display_pad_item', $query_type, $view_id );
									/**
									 * Execute an action after each of the View loop ghost last pad index is rendered.
									 *
									 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
									 * @param int $view_id The ID of the View being looped
									 * @since 2.7.3
									 */
									do_action( 'wpv_action_wpv_loop_after_display_pad_last_item', $query_type, $view_id );
								}
							} else {
								if ( isset( $item_indexes['pad'] ) ) {
									/** This action is documented above */
									do_action( 'wpv_action_wpv_loop_before_display_pad_item', $query_type, $view_id );
									$loop .= wpv_do_shortcode( $item_indexes['pad'] );
									/** This action is documented above */
									do_action( 'wpv_action_wpv_loop_after_display_pad_item', $query_type, $view_id );
								}
							}

							$i++;
						}
					}

					/**
					 * Execute an action after the View loop.
					 *
					 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
					 * @param int $view_id The ID of the View being looped
					 * @param array $args {
					 *     Loop arguments, if any, used when items are wrapped into rows.
					 *
					 *     @type int $wrap Optional. Number of items that each row sould include. If not set, all items will go into a single symbolic row.
					 *     @type bool $pad Optional. Whether the loop should include ghost items to complete a row, in case the items in a page do not cover the last row.
					 * }
					 * @since 2.7.3
					 */
					do_action( 'wpv_action_wpv_loop_after', $view_id, $query_type, $args );

					$WPVDebug->clean_index();

					$pagination_data = apply_filters( 'wpv_filter_wpv_get_pagination_settings', array(), $view_settings );
					if ( $pagination_data['effect'] == 'infinite' ) {
						$loop = '<!-- WPV_Infinite_Scroll --><!-- WPV_Infinite_Scroll_Insert -->' . $loop . '<!-- WPV_Infinite_Scroll -->';
					}

					/**
					 * Filters the View loop output before it is been replaced into the post content.
					 *
					 * @param string $loop       The View loop.
					 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'.
					 */
					$loop = apply_filters( 'wpv_filter_wpv_loop_before_post_content_replace', $loop, $view_id );

					/**
					 * Execute an action before the View loop has been replaced into the post content.
					 *
					 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
					 * @param int $view_id The ID of the View being looped
					 * @param array $args {
					 *     Loop arguments, if any, used when items are wrapped into rows.
					 *
					 *     @type int $wrap Optional. Number of items that each row sould include. If not set, all items will go into a single symbolic row.
					 *     @type bool $pad Optional. Whether the loop should include ghost items to complete a row, in case the items in a page do not cover the last row.
					 * }
					 */
					do_action( 'wpv_action_wpv_loop_before_post_content_replace', $view_id, $query_type, $args );



					// todo: Most probably this needs to go away.
					remove_filter( 'render_block', 'genesis_blocks_filter_container_block_for_amp', 10 );
					$post_content = function_exists( 'do_blocks' ) ?
						do_blocks( $post_content ) :
						$post_content;
					$out .= str_replace( $matches[0], $loop, $post_content );

					/**
					 * Execute an action after the View loop has been replaced into the post content.
					 *
					 * @param string $query_type The type of query for the current loop: 'posts', 'taxonomy', or 'users'
					 * @param int $view_id The ID of the View being looped
					 * @param array $args {
					 *     Loop arguments, if any, used when items are wrapped into rows.
					 *
					 *     @type int $wrap Optional. Number of items that each row sould include. If not set, all items will go into a single symbolic row.
					 *     @type bool $pad Optional. Whether the loop should include ghost items to complete a row, in case the items in a page do not cover the last row.
					 * }
					 */
					do_action( 'wpv_action_wpv_loop_after_post_content_replace', $view_id, $query_type, $args );

					// restore original $post
					$post = ( isset( $tmp_post ) && ( $tmp_post instanceof WP_Post ) ) ? clone $tmp_post : null;
					$authordata = ( isset( $tmp_authordata ) && is_object( $tmp_authordata ) ) ? clone $tmp_authordata : null;
					$id = $tmp_id;

				}

			} else {
				$out .= sprintf( '<!- %s ->', __( 'View not found', 'wpv-views' ) );
			}

		} else {

			if( $processed_views[ $view_caller_id ][ $hash ] !== true ) {
				// use output from cache
				$out .= $processed_views[ $view_caller_id ][ $hash ];
			}

		}

		return $out;
	}


	/**
	 * Get the html for each of the wpv-item index.
	 *
	 * <wpv-loop wrap=8 pad=true>
	 * Output for all items
	 * [wpv-item index=1]
	 * Output for item 1
	 * [wpv-item index=4]
	 * Output for item 4
	 * [wpv-item index=8]
	 * Output for item 8
	 * [wpv-item index=odd]
	 * Output for odd items (if they have no output defined by their order)
	 * [wpv-item index=even]
	 * Output for even items (if they have no output defined by their order)
	 * [wpv-item index=others]
	 * Output for other items
	 * [wpv-item index=pad]
	 * Output for when padding is required
	 * [wpv-item index=pad-last]
	 * Output for the last item when padding is required
	 * </wpv-loop>
	 *
	 * Will return an array with the output for each index.
	 *
	 * e.g. array('all' => 'Output for all items',
	 *		  '1' => 'Output for item 1',
	 *		  '4' => 'Output for item 4',
	 *		  '8' => 'Output for item 8',
	 *		  'other' => 'Output for other items',
	 *		  )
	 *
	 */
	function _get_item_indexes( $template ) {
		$indexes = array();
		$indexes['all'] = '';
		$indexes['pad'] = '';
		$indexes['pad-last'] = '';

		// search for the [wpv-item index=xx] shortcode
		$found = false;
		$last_index = -1;

		while( preg_match( '#\\[wpv-item index=([^\[]+)\]#is', $template, $matches ) ) {

			$pos = strpos( $template, $matches[0] );

			if ( !$found ) {
				// found the first one.
				// use all the stuff before for the all index.
				$indexes['all'] = substr( $template, 0, $pos );
				$found = true;
			} else if ( $last_index != -1 ) {
				// All the stuff before belongs to the previous index
				$indexes[ $last_index ] = substr( $template, 0, $pos );
			}

			$template = substr( $template, $pos + strlen( $matches[0] ) );

			$last_index = $matches[1];

		}

		if ( !$found ) {
			$indexes['all'] = $template;
		} else {
			$indexes[ $last_index ] = $template;
		}

		return $indexes;
	}


    /**
     * Determine whether an item in wpv-loop should be targeted by a split* index.
     *
     * @param int $split_factor Count of partitions of items inside wpv-loop. For example, value of 3 means that
     *     we want to split items in three partitions, thus two items - last item in the first third and last in
     *     the second third - will be targeted.
     * @param int $items_count Total count of items inside wpv-loop.
     * @param int $loop_index A zero-based index of current item inside wpv-loop.
     *
     * @return bool True if the item should be targeted.
     *
     * @since 1.11
     */
    private function is_split_index_match( $split_factor, $items_count, $loop_index ) {
        for( $i = 1; $i < $split_factor; ++$i ) {
            if( $loop_index + 1 == floor( $i * $items_count / $split_factor ) ) {
                return true;
            }
        }
        return false;
    }


    /**
     * Select (or re-select) index of wpv-item during processing wpv-loop, if it matches any
     * conditions split or odd/even indices.
     *
     * @param string $selected_index Currently selected index.
     * @param array $index_data
     *
     * @return string New selected index.
     *
     * @since unknown
     */
	function wpv_filter_wpv_item_loop_selected_index( $selected_index, $index_data ) {
		$loop_index	= $index_data['loop_index'];
		$item_index	= $index_data['item_index'];
		$avail_indexes = $index_data['avail_indexes'];
		$items_count = $index_data['items_count'];

        // Check indexes split2 to split5. If there's a wpv-item for an index, check if current item should be targeted.
        for( $split_factor = 2; $split_factor <= 5; ++$split_factor ) {
            $index = 'split' . $split_factor;
            if ( isset( $avail_indexes[ $index ] ) && $this->is_split_index_match( $split_factor, $items_count, $loop_index ) ) {
                return $index;
            }
        }

        // No split* index was selected, check the rest.
		if (
			isset( $avail_indexes['odd'] )
			&& ( $item_index % 2 == 1 )
		) {
			$selected_index = 'odd';
		} elseif (
			isset( $avail_indexes['even'] )
			&& ( $item_index % 2 == 0 )
		) {
			$selected_index = 'even';
		}

		return $selected_index;
	}


	/**
	 * Get the current post query.
	 */
	function get_query() {
		return $this->post_query;
	}


	/**
	 * Get all the views that have been created.
	 */
	function get_views() {
		$views = get_posts( array(
				'post_type' => 'view',
				'post_status' => 'publish',
				'numberposts' => -1 ) );
		return $views;
	}


	/**
	 * New method to get Content templates for module manager.
	 */
	function get_view_templates() {
		$view_templates = get_posts( array(
				'post_type' => 'view-template',
				'post_status' => 'publish',
				'numberposts' => -1 ) );
		return $view_templates;
	}

	// @deprecated - to delete - not used anywhere in Views
	function get_view_titles() {
		global $wpdb;
		static $views_available = null;
		if ( $views_available === null ) {
			$views_available = array();
			$views = $wpdb->get_results( "SELECT ID, post_title FROM {$wpdb->posts} WHERE post_type='view'" );
			foreach ( $views as $view ) {
				$views_available[ $view->ID ] = $view->post_title;
			}
		}
		return $views_available;
	}

	function wpv_get_postmeta_keys( $keys, $limit = 512 ) {
		$keys = $this->get_meta_keys( $limit );
		return $keys;
	}

    /**
	* Get visible custom field keys and hidden custom field keys declared as such
	*
	* @param int $cf_keys_limit maximum number of keys retrievable from database. Greater than 0.
	*
	* @since unknown
	*/
    function get_meta_keys( $cf_keys_limit = 512 ) {

        return $this->_get_meta_keys_internal( true, $cf_keys_limit );

    }

    /**
	* Get hidden custom field keys from database and Types
	*
	* @param int $cf_keys_limit maximum number of keys retrievable from database. Greater than 0.
	*
	* @since 1.10
	*/
    function get_hidden_meta_keys( $cf_keys_limit = 512 ) {

        return $this->_get_meta_keys_internal( false, $cf_keys_limit );

    }

    /**
	* Is this custom field visible?
	*
	* @param string $custom_field_key
	*
	* @return bool hidden fields declared as visible return true.
	*
	* @since 1.10
	*/
    private function custom_field_is_visible( $custom_field_key ) {

        static $cf_hidden_declared_visible = array();

        if( empty( $cf_hidden_declared_visible ) ) {

            global $WPV_settings;
            if( isset( $WPV_settings->wpv_show_hidden_fields ) && is_string( $WPV_settings->wpv_show_hidden_fields ) ) {
                $cf_hidden_declared_visible = explode( ',', $WPV_settings->wpv_show_hidden_fields );
            }
        }

        return substr( $custom_field_key, 0, 1 ) != '_' || in_array( $custom_field_key, $cf_hidden_declared_visible );
    }

    /**
	* Is this custom field hidden?
	*
	* @param string $custom_field_key name of the custom field.
	*
	* @return bool hidden fields declared as visible return true.
	*
	* @since 1.10
	*/
    private function custom_field_is_hidden( $custom_field_key ) {
        return substr( $custom_field_key, 0, 1 ) == '_';
    }

    /**
	 * Retrieve custom fields.
	 *
	 * @param bool $is_visible
	 * @param int $cf_keys_limit
	 * @return array
	 * @since 1.10
	 * @since 2.8.1 Offload the basic transient generation to the dedicated controllers.
	 */
    private function _get_meta_keys_internal( $is_visible = true, $cf_keys_limit = 512 ) {

        if ( $is_visible ) {
            $wpv_filter_keys_limit = 'wpv_filter_wpv_get_postmeta_keys_limit';
            $wpv_filter_keys_result = 'wpv_filter_wpv_get_postmeta_keys_result';
        } else {
            $wpv_filter_keys_limit = 'wpv_filter_wpv_get_hidden_postmeta_keys_limit';
            $wpv_filter_keys_result = 'wpv_filter_wpv_get_hidden_postmeta_keys_result';
        }

        // Filter limit. Allow 3rd parties increase or decrease the limit.
        $cf_keys_limit = apply_filters( $wpv_filter_keys_limit, $cf_keys_limit );

        // Verify it is still a number or revert to default
        if( ! is_int( $cf_keys_limit ) || $cf_keys_limit <= 0 ) {
            $cf_keys_limit = 512;
        }

        // Cache var
        // f(request_signature:string):array = request:array
        static $cf_keys_request_cache = array();
        $cf_request_signature = ( $is_visible ? 'visible' : 'hidden' ) . $cf_keys_limit;

		// We hard-cache default limit for visible and hidden fields when the limit is the default;
		// otherwise, we generate a query on-the-fly
		$cf_request_api = ( $is_visible ? 'wpv_get_visible_postmeta_cache' : 'wpv_get_hidden_postmeta_cache' );
		$cf_keys_request_cache[ $cf_request_signature ] = apply_filters( $cf_request_api, array(), $cf_keys_limit );

        // Filter result. Allow third-party developers add or remove elements.
        $cf_keys = apply_filters( $wpv_filter_keys_result, $cf_keys_request_cache[ $cf_request_signature ] );

        // Remove duplicates and sort result naturally.
        $cf_keys = array_unique( $cf_keys );
        // FIXME: Why is sorting done inside the method? (Legacy)
        if ( $cf_keys && is_array( $cf_keys ) ) {
            natcasesort( $cf_keys );
        }

        return $cf_keys;

	}

	function wpv_get_usermeta_keys( $keys, $limit = 512 ) {
		$keys = $this->get_usermeta_keys( $limit );
		return $keys;
	}

	/**
	* Get visible usermeta field keys
	*
	* @param int $usermeta_keys_limit maximum number of keys retrievable from database. Greater than 0.
	*
	* @since unknown
	*/
    function get_usermeta_keys( $usermeta_keys_limit = 512 ) {

        return $this->_get_usermeta_keys_internal( true, $usermeta_keys_limit );

    }

    /**
	* Get hidden usermeta field keys
	*
	* @param int $usermeta_keys_limit maximum number of keys retrievable from database. Greater than 0.
	*
	* @since 1.10
	*/
    function get_hidden_usermeta_keys( $usermeta_keys_limit = 512 ) {

        return $this->_get_usermeta_keys_internal( false, $usermeta_keys_limit );

    }

	/**
	* Is this custom field visible?
	*
	* @param string $usermeta_field_key
	*
	* @return bool hidden fields declared as visible return true.
	*
	* @since 1.10
	*/
    private function usermeta_field_is_visible( $usermeta_field_key ) {
        return substr( $usermeta_field_key, 0, 1 ) != '_';
    }

    /**
	* Is this custom field hidden?
	*
	* @param string $usermeta_field_key name of the custom field.
	*
	* @return bool hidden fields declared as visible return true.
	*
	* @since 1.10
	*/
    private function usermeta_field_is_hidden( $usermeta_field_key ) {
        return substr( $usermeta_field_key, 0, 1 ) == '_';
    }

	/**
	* Is this custom field hidden?
	*
	* @param string $usermeta_field_key name of the custom field.
	*
	* @return bool hidden fields declared as visible return true.
	*
	* @since 1.10
	*/
    private function usermeta_field_is_skipped( $usermeta_field_key ) {
		$return = true;
		// Exclude these keys
		// phpcs:disable WordPress.Arrays.ArrayDeclarationSpacing.ArrayItemNoNewLine
		$hidden_usermeta = array(
			'first_name', 'last_name', 'name', 'nickname', 'description', 'yim', 'jabber', 'aim',
			'rich_editing', 'comment_shortcuts', 'admin_color', 'use_ssl', 'show_admin_bar_front',
			'capabilities', 'user_level', 'user-settings',
			'dismissed_wp_pointers','show_welcome_panel',
			'dashboard_quick_press_last_post_id', 'managenav-menuscolumnshidden',
			'primary_blog', 'source_domain',
			'closedpostboxes', 'metaboxhidden', 'meta-box-order_dashboard', 'meta-box-order', 'nav_menu_recently_edited',
			'new_date', 'show_highlight', 'language_pairs',
			'module-manager',
			'screen_layout', 'session_tokens',
			'hide_wpcf_welcome_panel',
		);
		if ( in_array( $usermeta_field_key, $hidden_usermeta ) ) {
			$return = false;
		}

        return $return;
    }

	/**
	 * Retrieve custom fields.
	 *
	 * @param bool $is_visible
	 * @param int $usermeta_keys_limit
	 * @return array custom field keys
	 * @since 1.10
	 */
    private function _get_usermeta_keys_internal( $is_visible = true, $usermeta_keys_limit = 512 ) {
        if ( $is_visible ) {
            $wpv_filter_keys_limit = 'wpv_filter_wpv_get_usermeta_keys_limit';
            $wpv_filter_keys_result = 'wpv_filter_wpv_get_usermeta_keys_result';
        } else {
            $wpv_filter_keys_limit = 'wpv_filter_wpv_get_hidden_usermeta_keys_limit';
            $wpv_filter_keys_result = 'wpv_filter_wpv_get_hidden_usermeta_keys_result';
        }

        $cf_keys = array();

        // Filter limit. Allow 3rd parties increase or decrease the limit.
        $usermeta_keys_limit = apply_filters( $wpv_filter_keys_limit, $usermeta_keys_limit );

        // Verify it is still a number or revert to default
        if( ! is_int( $usermeta_keys_limit ) || $usermeta_keys_limit <= 0 ) {
            $usermeta_keys_limit = 512;
        }

        // Cache var
        // f(request_signature:string):array = request:array
        static $usermeta_keys_request_cache = array();
        $usermeta_request_signature = ( $is_visible ? 'visible' : 'hidden' ) . $usermeta_keys_limit;


		// We hard-cache default limit for visible and hidden fields when the limit is the default;
		// otherwise, we generate a query on-the-fly
		$usermeta_request_api = ( $is_visible ? 'wpv_get_visible_usermeta_cache' : 'wpv_get_hidden_usermeta_cache' );
		$usermeta_keys_request_cache[ $usermeta_request_signature ] = apply_filters( $usermeta_request_api, array(), $usermeta_keys_limit );

        // Filter result. Allow third-party developers add or remove elements.
        $um_keys = apply_filters( $wpv_filter_keys_result, $usermeta_keys_request_cache[ $usermeta_request_signature ] );

        // Remove duplicates and sort result naturally.
        $um_keys = array_unique( $um_keys );
        // FIXME: Why is sorting done inside the method? (Legacy)
        if ( $um_keys && is_array( $um_keys ) ) {
            natcasesort( $um_keys );
        }

        return $um_keys;

	}

	function wpv_get_termmeta_keys( $keys, $limit = 512 ) {
		$keys = $this->get_termmeta_keys( $limit );
		return $keys;
	}

	/**
	* Get visible termmeta field keys and hidden termmeta field keys declared as such
	*
	* @param int $cf_keys_limit maximum number of keys retrievable from database. Greater than 0.
	*
	* @since 1.12
	*/
    function get_termmeta_keys( $termmeta_keys_limit = 512 ) {

        return $this->_get_termmeta_keys_internal( true, $termmeta_keys_limit );

    }

    /**
	* Get hidden termmeta field keys from database and Types
	*
	* @param int $cf_keys_limit maximum number of keys retrievable from database. Greater than 0.
	*
	* @since 1.12
	*/
    function get_hidden_termmeta_keys( $termmeta_keys_limit = 512 ) {

        return $this->_get_termmeta_keys_internal( false, $termmeta_keys_limit );

    }

    /**
	* Is this termmeta field visible?
	*
	* @param string $termmeta_field_key
	*
	* @return bool hidden fields declared as visible return true.
	*
	* @since 1.12
	*/
    private function termmeta_field_is_visible( $termmeta_field_key ) {

        static $termmeta_hidden_declared_visible = array();

        return substr( $termmeta_field_key, 0, 1 ) != '_' || in_array( $termmeta_field_key, $termmeta_hidden_declared_visible );
    }

    /**
	* Is this termmeta field hidden?
	*
	* @param string $termmeta_field_key name of the termmeta field.
	*
	* @return bool hidden fields declared as visible return true.
	*
	* @since 1.12
	*/
    private function termmeta_field_is_hidden( $termmeta_field_key ) {
        return substr( $termmeta_field_key, 0, 1 ) == '_';
    }

    /**
	 * Retrieve termmeta fields.
	 *
	 * @param bool $is_visible
	 * @param int $termmeta_keys_limit
	 * @return array termmeta field keys
	 * @since 1.12
	 */
    private function _get_termmeta_keys_internal( $is_visible = true, $termmeta_keys_limit = 512 ) {
		global $wp_version;
		if ( version_compare( $wp_version, '4.4' ) < 0 ) {
			return array();
		}

        if ( $is_visible ) {
            $wpv_filter_keys_limit = 'wpv_filter_wpv_get_termmeta_keys_limit';
            $wpv_filter_keys_result = 'wpv_filter_wpv_get_termmeta_keys_result';
        } else {
            $wpv_filter_keys_limit = 'wpv_filter_wpv_get_hidden_termmeta_keys_limit';
            $wpv_filter_keys_result = 'wpv_filter_wpv_get_hidden_termmeta_keys_result';
        }

        $termmeta_keys = array();

        // Filter limit. Allow 3rd parties increase or decrease the limit.
        $termmeta_keys_limit = apply_filters( $wpv_filter_keys_limit, $termmeta_keys_limit );

        // Verify it is still a number or revert to default
        if( ! is_int( $termmeta_keys_limit ) || $termmeta_keys_limit <= 0 ) {
            $termmeta_keys_limit = 512;
        }

        // Cache var
        // f(request_signature:string):array = request:array
        static $termmeta_keys_request_cache = array();
        $termmeta_request_signature = ( $is_visible ? 'visible' : 'hidden' ) . $termmeta_keys_limit;

		// We hard-cache default limit for visible and hidden fields when the limit is the default;
		// otherwise, we generate a query on-the-fly
		$termmeta_request_api = ( $is_visible ? 'wpv_get_visible_termmeta_cache' : 'wpv_get_hidden_termmeta_cache' );
		$termmeta_keys_request_cache[ $termmeta_request_signature ] = apply_filters( $termmeta_request_api, array(), $termmeta_keys_limit );

        // Filter result. Allow third-party developers add or remove elements.
        $termmeta_keys = apply_filters( $wpv_filter_keys_result, $termmeta_keys_request_cache[ $termmeta_request_signature ] );

        // Remove duplicates and sort result naturally.
        $termmeta_keys = array_unique( $termmeta_keys );
        // FIXME: Why is sorting done inside the method? (Legacy)
        if ( $termmeta_keys && is_array( $termmeta_keys ) ) {
            natcasesort( $termmeta_keys );
        }

        return $termmeta_keys;
	}

    /**
     * Retrieve $WPV_Settings_Screen (array-like)
     * @deprecated since version 1.8
     * @return \WPV_Settings_Screen
     */
	function get_options() {
        return WPV_Settings::get_instance();
    }

    /**
     * Bulk set settings and save
     * @deprecated since version 1.8
     * @param array $options
     */
    function save_options( $options ) {
        global $WPV_settings;
        if ( is_array( $options ) ) {
            $WPV_settings->set( $options );
        }
        $WPV_settings->save();
    }

	function is_embedded() {
		return true;
	}

	function get_current_taxonomy_term() {
		if ( isset( $this->taxonomy_data['term'] ) ) {
			return $this->taxonomy_data['term'];
		} else {
			return null;
		}
	}


	function taxonomy_query( $view_settings ) {
		$items = get_taxonomy_query( $view_settings );

		$this->taxonomy_data['item_count'] = sizeof( $items );

		if ( $view_settings['pagination']['type'] == 'disabled' ) {
			$this->taxonomy_data['max_num_pages'] = 1;
			$this->taxonomy_data['item_count_this_page'] = $this->taxonomy_data['item_count'];
		} else {
			$posts_per_page = $view_settings['pagination']['posts_per_page'];
			$this->taxonomy_data['items_per_page'] = $posts_per_page;
			$this->taxonomy_data['max_num_pages'] = ceil( $this->taxonomy_data['item_count'] / $posts_per_page );
			if ( $this->taxonomy_data['item_count'] > $posts_per_page ) {
				$page = 1;
				if (
					isset( $_GET['wpv_paged'] )
					&& isset( $_GET['wpv_view_count'] )
					&& esc_attr( $_GET['wpv_view_count'] ) == apply_filters( 'wpv_filter_wpv_get_object_unique_hash', '', $view_settings )
				) {
					// @todo check this against the View hash too!
					$page = (int) $_GET['wpv_paged'];
				}
				$this->taxonomy_data['page_number'] = $page;
				$items = array_slice( $items, ($page - 1) * $posts_per_page, $posts_per_page );
			}
		}

		$this->taxonomy_data['item_count_this_page'] = sizeof( $items );
		return $items;
	}


	/**
	 * Get Users query,
	 */
	function users_query( $view_settings ) {
		$items = get_users_query( $view_settings );

		$this->users_data['item_count'] = sizeof( $items );

		if ( $view_settings['pagination']['type'] == 'disabled' ) {
			$this->users_data['item_count_this_page'] = $this->users_data['item_count'];
			$this->users_data['max_num_pages'] = 1;
		} else {
			$posts_per_page = $view_settings['pagination']['posts_per_page'];
			$this->users_data['items_per_page'] = $posts_per_page;
			$this->users_data['max_num_pages'] = ceil( $this->users_data['item_count'] / $posts_per_page );
			if ( $this->users_data['item_count'] > $posts_per_page ) {
				$page = 1;
				if (
					isset( $_GET['wpv_paged'] )
					&& isset( $_GET['wpv_view_count'] )
					&& esc_attr( $_GET['wpv_view_count'] ) == apply_filters( 'wpv_filter_wpv_get_object_unique_hash', '', $view_settings )
				) {
					$page = (int) $_GET['wpv_paged'];
				}
				$this->users_data['page_number'] = $page;
				$items = array_slice( $items, ($page - 1) * $posts_per_page, $posts_per_page );
			}
		}

		$this->users_data['item_count_this_page'] = sizeof( $items );
		return $items;
	}

	function wpv_get_query_type( $query_type = 'posts', $view_id = null ) {
		$query_type = $this->get_query_type( $view_id );
		return $query_type;
	}

	/**
	 * Get query type for given or current View/WPA.
	 *
	 * @param null|int $view_id ID of existing View/WPA or null to use the current one.
	 * @return string Query type, which means 'posts', 'taxonomy' or 'users'.
	 * @since 1.11
	 * @since 2.4.0 Return an empty string when the passed View ID does not match a valid View.
	 * @since 2.9.3 Cache the outcome, per View, so this only runs once.
	 */
	function get_query_type( $view_id = null ) {
		if ( is_null( $view_id ) ) {
			$view_id = $this->get_current_view();
		}

		$cached_result = toolset_getarr( $this->cache_query_type, $view_id, null );

		if ( null !== $cached_result ) {
			return $cached_result;
		}

		$view = WPV_View_Base::get_instance( $view_id );
		if ( is_null( $view ) ) {
			$result = '';
		} else {
			$result = $view->query_type;
		}

		$this->cache_query_type[ $view_id ] = $result;
		return $result;
	}


	function wpv_get_current_page_number( $page = 1 ) {
		$page = $this->get_current_page_number();
		return $page;
	}


	function get_current_page_number() {
		$query_type = $this->get_query_type();
		if (
			$query_type == 'taxonomy'
			&& isset( $this->taxonomy_data )
			&& isset( $this->taxonomy_data['page_number'] )
		) {
			return $this->taxonomy_data['page_number'];
		} else if (
			$query_type == 'users'
			&& isset( $this->users_data )
			&& isset( $this->users_data['page_number'] )
		) {
			return $this->users_data['page_number'];
		} else if (
			$query_type == 'posts'
			&& $this->post_query
		) {
			return ( ! empty( $this->post_query->query_vars['paged'] ) ) ? (int) $this->post_query->query_vars['paged'] : 1;
		} else {
			return 1;
		}
		return 1;
	}

	function wpv_get_max_pages( $max_pages = 1 ) {
		$max_pages = $this->get_max_pages();
		return $max_pages;
	}

	function get_max_pages() {
		$query_type = $this->get_query_type();
		if (
			$query_type == 'taxonomy'
			&& isset( $this->taxonomy_data )
			&& isset( $this->taxonomy_data['max_num_pages'] )
		) {
			return $this->taxonomy_data['max_num_pages'];
		} else if (
			$query_type == 'users'
			&& isset( $this->users_data )
			&& isset( $this->users_data['max_num_pages'] )
		) {
			return $this->users_data['max_num_pages'];
		} else if (
			$query_type == 'posts'
			&& $this->post_query
		) {
			return $this->post_query->max_num_pages;
		} else {
			return 1;
		}
		return 1;
	}

	function wpv_get_taxonomy_found_count( $count = 0 ) {
		$count = $this->get_taxonomy_found_count();
		return $count;
	}

	function get_taxonomy_found_count() {
		if ( isset( $this->taxonomy_data['item_count'] ) ) {
			return $this->taxonomy_data['item_count'];
		} else {
			return 0;
		}
	}

	function wpv_get_users_found_count( $count = 0 ) {
		$count = $this->get_users_found_count();
		return $count;
	}


	function get_users_found_count() {
		if ( isset( $this->users_data['item_count'] ) ) {
			return $this->users_data['item_count'];
		} else {
			return 0;
		}
	}


	function get_parent_view_taxonomy() {
		return $this->parent_taxonomy;
	}

	function wpv_get_parent_view_taxonomy( $parent_taxonomy = null ) {
		$maybe_parent_taxonomy = $this->get_parent_view_taxonomy();
		if ( $maybe_parent_taxonomy ) {
			$parent_taxonomy = $maybe_parent_taxonomy;
		}
		return $parent_taxonomy;
	}

	function wpv_set_parent_view_taxonomy( $parent_taxonomy ) {
		$this->parent_taxonomy = $parent_taxonomy;
	}

	function get_parent_view_user() {
		return $this->parent_user;
	}

	function wpv_get_parent_view_user( $parent_user = null ) {
		$maybe_parent_user = $this->get_parent_view_user();
		if ( $maybe_parent_user ) {
			$parent_user = $maybe_parent_user;
		}
		return $parent_user;
	}

	function wpv_set_parent_view_user( $parent_user ) {
		$this->parent_user = $parent_user;
	}



	function wpv_get_widget_view_id( $widget_view_id = 0 ) {
		$widget_view_id = $this->get_widget_view_id();
		return $widget_view_id;
	}

	function wpv_set_widget_view_id( $widget_view_id ) {
		$this->widget_view_id = $widget_view_id;
	}

	function get_widget_view_id() {
		return $this->widget_view_id;
	}

	function set_widget_view_id( $widget_view_id ) {
		$this->widget_view_id = $widget_view_id;
	}


	function set_variable( $name, $value ) {
		$this->variables[ $name ] = $value;
	}


	function get_variable( $name ) {
		if ( strpos( $name, '$' ) === 0 ) {
			$name = substr( $name, 1 );

			if ( isset( $this->variables[ $name ] ) ) {
				return $this->variables[ $name ];
			}
		}
		return null;
	}

	/**
	* This might be deprecated, but does not hurt
	* Maybe add a _doing_it_wrong call_user_func
	*/
	function get_view_shortcode_params( $view_id ) {
		$settings = $this->get_view_settings( $view_id );

		$params = wpv_get_custom_field_view_params( $settings );
		$params = array_merge( $params, wpv_get_taxonomy_view_params( $settings ) );

		return $params;
	}

	/**
	 * Check if a View has any search from controls.
	 *
	 * @param int $view_id
	 * @return bool
	 * @since unknown
	 * @since 2.9.3 Cache the check so we avoid doing it more than once for a given View.
	 */
	public function does_view_have_form_controls( $view_id ) {
		$has_form_controls = toolset_getarr( $this->cache_view_ids_check_for_form_controls, $view_id, null );
		if ( null !== $has_form_controls ) {
			return $has_form_controls;
		}

		$view_settings = $this->get_view_settings( $view_id );

		// Sometimes, the above check is not enough because the filters have been deleted => search for the actual controls shortcodes
		if ( isset( $view_settings['filter_meta_html'] ) ) {
			if ( strpos( $view_settings['filter_meta_html'], "[wpv-control" )
				|| strpos( $view_settings['filter_meta_html'], "[wpv-filter-search-box" )
				|| strpos( $view_settings['filter_meta_html'], "[wpv-filter-submit" ) )
			{
				$this->cache_view_ids_check_for_form_controls[ $view_id ] = true;
				return true;
			}
		}

		$this->cache_view_ids_check_for_form_controls[ $view_id ] = false;
		return false;
	}

	/**
	* does_view_have_form_control_with_submit
	*
	* See if a view has any enabled from controls and packs a submit button
	*
	* @param $view_id integer
	*
	* @return boolean
	*
	* @since 1.7.0
	*/

	function does_view_have_form_control_with_submit( $view_id ) {
		$view_settings = $this->get_view_settings( $view_id );

		if ( isset( $view_settings['filter_meta_html'] ) ) {
			if (
				(
					strpos( $view_settings['filter_meta_html'], "[wpv-control" )
					|| strpos( $view_settings['filter_meta_html'], "[wpv-filter-search-box" )
					|| strpos( $view_settings['filter_meta_html'], "[wpv-filter-submit" )
				)
				&& strpos( $view_settings['filter_meta_html'], '[wpv-filter-submit' )
			) {
				return true;
			}
		}

		return false;
	}


	/**
	 * Check whether a View post object is actually a WordPress Archive.
	 *
	 * @param int $view_id
	 * @return bool
	 * @since unknoen
	 * @since 2.9.3 Cache the results so we only check each View once.
	 */
	function is_archive_view( $view_id ) {
		$cached_result = toolset_getarr( $this->is_archive_view, $view_id, null );

		if ( null !== $cached_result ) {
			return $cached_result;
		}

		$view_settings = $this->get_view_settings( $view_id );
		if ( ! isset( $view_settings['view-query-mode'] ) ) {
			$view_settings['view-query-mode'] = 'normal';
		}
		$archive_query_modes = array( 'archive', 'layouts-loop' );

		/**
		 * Filter the array of valid WPA view-query-mode values
		 *
		 * @param array $archive_query_modes The array of valid values
		 * @since 1.7
		 */
		$archive_query_modes = apply_filters( 'wpv_filter_allowed_archive_query_modes', $archive_query_modes );
		$result = in_array( $view_settings['view-query-mode'], $archive_query_modes, true );

		$this->is_archive_view[ $view_id ] = $result;
		return $result;
	}


	function wpv_format_date() {
		$date_format = $_POST['date-format'];
		if ( $date_format == '' ) {
			$date_format = get_option( 'date_format' );
		}
		// this is needed to escape characters in the date_i18n function
		$date_format = str_replace( '\\\\', '\\', $date_format );
		$date = $_POST['date'];
		// We can not be sure that the adodb_xxx functions are available, so we do different things whether they exist or not
		if ( defined( 'ADODB_DATE_VERSION' ) ) {
			$date = adodb_mktime( 0, 0, 0, substr( $date, 2, 2 ), substr( $date, 0, 2 ), substr( $date, 4, 4 ) );
			echo json_encode( array(
					'display' => adodb_date( $date_format, $date ),
					'timestamp' => $date ) );
		} else {
			$date = mktime( 0, 0, 0, substr( $date, 2, 2 ), substr( $date, 0, 2 ), substr( $date, 4, 4 ) );
			echo json_encode( array(
					'display' => date_i18n( $date_format, intval( $date ) ),
					'timestamp' => $date ) );
		}

		die();
	}

	/**
	 * Enqueue jQuery in case the current page has a View.
	 *
	 * @since 2.9.2
	 */
	public function wpv_meta_html_extra_dependencies() {
		$view_ids = array_unique( $this->view_used_ids );
		if ( empty( $view_ids ) ) {
			return;
		}

		wp_enqueue_script( 'jquery' );
	}

	/**
	 * Gets the extra CSS for the used View IDs.
	 *
	 * @param string $cssout
	 * @return string
	 */
	public function get_meta_html_extra_css( $cssout = '' ) {
		$view_ids = array_unique( $this->view_used_ids );

		foreach ( $view_ids as $view_id ) {
			$meta = $this->get_view_settings( $view_id );
			$is_wpa = $this->is_archive_view( $view_id );
			$cssout_item = '';
			if (
				isset( $meta['filter_meta_html_css'] )
				&& '' != $meta['filter_meta_html_css']
			) {
				$cssout_item .= $meta["filter_meta_html_css"] . "\n";
			}
			if (
				isset( $meta['layout_meta_html_css'] )
				&& '' != $meta['layout_meta_html_css']
			) {
				$cssout_item .= $meta["layout_meta_html_css"] . "\n";
			}
			if ( '' != $cssout_item ) {
				$cssout_item_title = get_post_field( 'post_name', get_post( $view_id ) );
				$cssout .= "/* ----------------------------------------- */\n";
				if ( $is_wpa ) {
					/* translators: Text for the opening comment block for the Custom CSS of a WordPress Archive. */
					$cssout .= '/* ' . esc_html( sprintf( __( 'WordPress Archive slug: %s - start', 'wpv-views' ), $cssout_item_title ) ) . " */\n";
				} else {
					/* translators: Text for the opening comment block for the Custom CSS of a View. */
					$cssout .= '/* ' . esc_html( sprintf( __( 'View slug: %s - start', 'wpv-views' ), $cssout_item_title ) ) . " */\n";
				}
				$cssout .= "/* ----------------------------------------- */\n";
				$cssout .= $cssout_item;
				$cssout .= "/* ----------------------------------------- */\n";
				if ( $is_wpa ) {
					/* translators: Text for the closing comment block for the Custom CSS of a WordPress Archive. */
					$cssout .= '/* ' . esc_html( sprintf( __( 'WordPress Archive slug: %s - end', 'wpv-views' ), $cssout_item_title ) ) . " */\n";
				} else {
					/* translators: Text for the closing comment block for the Custom CSS of a View. */
					$cssout .= '/* ' . esc_html( sprintf( __( 'View slug: %s - end', 'wpv-views' ), $cssout_item_title ) ) . " */\n";
				}
				$cssout .= "/* ----------------------------------------- */\n";
			}
		}

		return $cssout;
	}

	public function wpv_meta_html_extra_css() {
		$view_ids = array_unique( $this->view_used_ids );
		if ( empty( $view_ids ) ) {
			return;
		}

		$extra_css = '';

		$extra_css .= $this->get_meta_html_extra_css();

		$extra_css .= "<!--[if IE 7]><style>\n"
				. ".wpv-pagination { *zoom: 1; }\n"
				. "</style><![endif]-->\n";

		$extra_css = preg_replace( '~\R~u', '', addslashes( $extra_css ) );

		$vanilla_js_for_css_out = "\n<script type=\"text/javascript\">\n";
		$vanilla_js_for_css_out .= 'const wpvViewHead = document.getElementsByTagName( "head" )[ 0 ];' . "\n";
		$vanilla_js_for_css_out .= 'const wpvViewExtraCss = document.createElement( "style" );' . "\n";
		$vanilla_js_for_css_out .= 'wpvViewExtraCss.textContent = \'' . $extra_css . '\';' . "\n";
		$vanilla_js_for_css_out .= 'wpvViewHead.appendChild( wpvViewExtraCss );' . "\n";
		$vanilla_js_for_css_out .= "</script>\n";
		echo $vanilla_js_for_css_out;
	}

	function wpv_meta_html_extra_js() {
		$view_ids = array_unique( $this->view_used_ids );
		if ( empty( $view_ids ) ) {
			return;
		}

		$jsout = '';
		foreach ( $view_ids as $view_id ) {
			$meta = $this->get_view_settings( $view_id );
			$is_wpa = $this->is_archive_view( $view_id );
			$jsout_item = '';
			if (
				isset( $meta['filter_meta_html_js'] )
				&& '' != $meta['filter_meta_html_js']
			) {
				$jsout_item .= $meta["filter_meta_html_js"] . "\n";
			}
			if (
				isset( $meta['layout_meta_html_js'] )
				&& '' != $meta['layout_meta_html_js']
			) {
				$jsout_item .= $meta["layout_meta_html_js"] . "\n";
			}
			if ( '' != $jsout_item ) {
				$jsout_item_title = get_post_field( 'post_name', get_post( $view_id ) );
				$jsout .= "//-----------------------------------------\n";
				if ( $is_wpa ) {
					/* translators: Text for the opening comment block for the Custom JS of a WordPress Archive. */
					$jsout .= '// ' . esc_html( sprintf( __( 'WordPress Archive slug: %s - start', 'wpv-views' ), $jsout_item_title ) ) . "\n";
				} else {
					/* translators: Text for the opening comment block for the Custom JS of a View. */
					$jsout .= '// ' . esc_html( sprintf( __( 'View slug: %s - start', 'wpv-views' ), $jsout_item_title ) ) . "\n";
				}
				$jsout .= "//-----------------------------------------\n";
				$jsout .= $jsout_item;
				$jsout .= "//-----------------------------------------\n";
				if ( $is_wpa ) {
					/* translators: Text for the closing comment block for the Custom JS of a WordPress Archive. */
					$jsout .= '// ' . esc_html( sprintf( __( 'WordPress Archive slug: %s - end', 'wpv-views' ), $jsout_item_title ) ) . "\n";
				} else {
					/* translators: Text for the closing comment block for the Custom JS of a View. */
					$jsout .= '// ' . esc_html( sprintf( __( 'View slug: %s - end', 'wpv-views' ), $jsout_item_title ) ) . "\n";
				}
				$jsout .= "//-----------------------------------------\n";
			}
		}
		if ( '' != $jsout ) {
			echo "\n<script type=\"text/javascript\">\n" . $jsout . "</script>\n";
		}
	}

	/**
	* wpv_additional_js_files
	*
	* Add custom script URLs from the View layout settings into the wp_footer action
	*
	* @since 1.8.0
	*/

	function wpv_additional_js_files() {
		$view_ids = array_unique( $this->view_used_ids );
		foreach ( $view_ids as $view_id ) {
			$meta = $this->get_view_layout_settings( $view_id );
			if (
				isset( $meta['additional_js'] )
				&& ! empty( $meta['additional_js'] )
			) {
				$scripts = explode( ',', $meta['additional_js'] );
				foreach ( $scripts as $script ) {
					if ( strpos( $script, '[theme]' ) === 0 ) {
						$script = str_replace( '[theme]', get_stylesheet_directory_uri(), $script );
					}
					echo "\n";
					?>
					<script type="text/javascript" src="<?php echo esc_url( $script ); ?>"></script>
					<?php
					echo "\n";
				}
			}
		}
	}

	function wpv_register_assets() {

		$views_global_settings = WPV_Settings::get_instance();
		$wpv_ajax = WPV_Ajax::get_instance();

		/* ---------------------------- /*
		/* BACKEND SCRIPTS
		/* ---------------------------- */

        // URI.js
        // @todo move to common
        if( ! wp_script_is( 'toolset-uri-js', 'registered' ) ) {
            wp_register_script( 'toolset-uri-js', WPV_URL_EMBEDDED . '/res/js/uri-js/URI.min.js', array(), WPV_VERSION );
        }
        if( ! wp_script_is( 'toolset-uri-js-jquery-plugin', 'registered' ) ) {
            wp_register_script( 'toolset-uri-js-jquery-plugin', WPV_URL_EMBEDDED . '/res/js/uri-js/jquery.URI.min.js', array( 'jquery', 'toolset-uri-js' ), WPV_VERSION );
        }

		// CodeMirror
		wp_register_script(
			'views-codemirror-conf-script',
			WPV_URL_EMBEDDED . '/res/js/views_codemirror_conf.js',
			array(
				'jquery',
				'toolset-event-manager',
				'toolset-codemirror-script',
				'toolset-meta-html-codemirror-overlay-script',
				'toolset-meta-html-codemirror-xml-script',
				'toolset-meta-html-codemirror-css-script',
				'toolset-meta-html-codemirror-js-script',
				'toolset-meta-html-codemirror-utils-search-cursor',
				'toolset-meta-html-codemirror-utils-panel'
			),
			WPV_VERSION,
			false
		);

		// DEPRECATED
		// Keep views-select2-script because the installed version of other plugin might be using it - just register, never enqueue
		// TO DEPRECATE
		wp_register_script( 'views-select2-script', TOOLSET_COMMON_PATH . '/res/lib/select2/select2.min.js', array( 'jquery' ), WPV_VERSION );

		// Views utils script
		wp_register_script( 'views-utils-script', WPV_URL_EMBEDDED . '/res/js/lib/utils.js', array( 'jquery', 'toolset_select2', 'toolset-utils' ), WPV_VERSION );
		$help_box_translations = array(
				'wpv_dont_show_it_again' => __( "Got it! Don't show this message again", 'wpv-views'),
				'wpv_close' => __( 'Close', 'wpv-views') );
		wp_localize_script( 'views-utils-script', 'wpv_help_box_texts', $help_box_translations );

		// Shortcodes GUI script
		global $pagenow, $post;
		wp_register_script(
			'views-shortcodes-gui-script',
			WPV_URL_EMBEDDED . '/res/js/views_shortcodes_gui.js',
			array( Toolset_Assets_Manager::SCRIPT_TOOLSET_SHORTCODE, 'quicktags' ),
			WPV_VERSION
		);
		$shortcodes_gui_translations = array(
			'mce' => array(
				'views' => array(
					'button' => __( 'Fields and Views', 'wpv-views' ),
					'canEdit' => current_user_can( EDIT_VIEWS ),
					'editViewLink' => admin_url( 'admin.php?page=views-editor' ),
					'editViewLabel' => __( 'Edit this View', 'wpv-views' ),
					'editTemplateLink' => admin_url( 'admin.php?page=ct-editor' ),
					'editTemplateLabel' => __( 'Edit this Content Template', 'wpv-views' ),
					'removeLabel' => __( 'Remove this item', 'wpv-views' ),
					'missingObject' => __( 'This item does not exist anymore', 'wpv-views' ),
				),
				'conditional' => array(
					'button' =>  __( 'Conditional output', 'wpv-views' ),
				),
			),
			'ajax' => array(
				'getConditionalOutputDialogData' => array(
					'action' => $wpv_ajax->get_action_js_name( WPV_Ajax::CALLBACK_GET_CONDITIONAL_OUTPUT_DIALOG_DATA ),
					'nonce' => wp_create_nonce( WPV_Ajax::CALLBACK_GET_CONDITIONAL_OUTPUT_DIALOG_DATA ),
				),
			),
			'dialogs' => array(
				'fields_and_views' => array(
					'title' => __( 'Toolset - Fields and Views', 'wpv-views' ),
				),
				'generated' => array(
					'title' => __( 'Toolset - generated shortcode', 'wpv-views' )
				),
			),
			'wpv_insert_shortcode'						=> __( 'Insert shortcode', 'wpv-views'),
			'wpv_create_shortcode'						=> __( 'Create shortcode', 'wpv-views' ),
			'wpv_update_shortcode'						=> __( 'Update shortcode', 'wpv-views' ),
			'wpv_save_settings'							=> __( 'Save settings', 'wpv-views' ),
			'wpv_close'									=> __( 'Close', 'wpv-views'),
			'wpv_cancel'								=> __( 'Cancel', 'wpv-views' ),
			'wpv_back'									=> __( 'Back', 'wpv-views' ),
			'wpv_edit'									=> __( 'Edit', 'wpv-views' ),
			'wpv_fields_and_views_title'				=> __( 'Fields and Views shortcodes', 'wpv-views' ),
			'wpv_fields_and_views_button_title'			=> __( 'Fields and Views', 'wpv-views' ),
			'wpv_previous'								=> __( 'Previous', 'wpv-views' ),
			'wpv_next'									=> __( 'Next', 'wpv-views' ),
			'loading_options'							=> __( 'Loading...', 'wpv-views' ),
			'nonce_error'								=> __( 'Security verification failed, please reload the page and try again', 'wpv-views' ),
			'attr_number_invalid'						=> __( 'Please enter a valid number', 'wpv-views' ),
			'attr_numberlist_invalid'					=> __( 'Please enter a valid comma separated number list', 'wpv-views' ),
			'attr_year_invalid'							=> __( 'Please enter a valid four-digits year, like 2015', 'wpv-views' ),
			'attr_month_invalid'						=> __( 'Please enter a valid month number (1-12)', 'wpv-views' ),
			'attr_week_invalid'							=> __( 'Please enter a valid week number (1-53)', 'wpv-views' ),
			'attr_day_invalid'							=> __( 'Please enter a valid day number (1-31)', 'wpv-views' ),
			'attr_hour_invalid'							=> __( 'Please enter a valid hour (0-23)', 'wpv-views' ),
			'attr_minute_invalid'						=> __( 'Please enter a valid minute (0-59)', 'wpv-views' ),
			'attr_second_invalid'						=> __( 'Please enter a valid second (0-59)', 'wpv-views' ),
			'attr_dayofyear_invalid'					=> __( 'Please enter a valid day of the year (1-366)', 'wpv-views' ),
			'attr_dayofweek_invalid'					=> __( 'Please enter a valid day of the week (1-7)', 'wpv-views' ),
			'attr_url_invalid'							=> __( 'Please enter a valid URL', 'wpv-views' ),
			'attr_empty'								=> __( 'This field is required', 'wpv-views' ),
            'wpv_conditional_button'					=> __( 'Conditional output', 'wpv-views' ),
			'conditional_enter_conditions_manually'		=> __( 'Edit conditions manually', 'wpv-views' ),
			'conditional_enter_conditions_gui'			=> __( 'Edit conditions using the GUI', 'wpv-views' ),
			'conditional_switch_alert'					=> __( 'Your custom conditions will be lost if you switch back to GUI editing.', 'wpv-views' ),
            'wpv_editor_callback_nonce'        			=> wp_create_nonce('wpv_editor_callback'),
			'ajaxurl'									=> wpv_get_views_ajaxurl(),
			'pagenow'									=> $pagenow,
			'get_page' => toolset_getget( 'page' ),
		);

		$views_shortcodes_gui_data = apply_filters( 'wpv_filter_wpv_shortcodes_gui_data', array() );
		$shortcodes_gui_translations['shortcodes_with_gui'] = array_keys( $views_shortcodes_gui_data );

		$shortcodes_gui_translations['post_id'] = 0;
		if (
			in_array( $pagenow, array( 'post.php' ) )
			&& isset( $_GET["post"] )
		) {
			$shortcodes_gui_translations['post_id'] = (int) $_GET["post"];
		} else {
			if (
				isset( $post )
				&& is_object( $post )
				&& isset( $post->ID )
			) {
				$shortcodes_gui_translations['post_id'] = $post->ID;
			}
		}

		/**
		 * Filter the i18n data for the views-shortcodes-gui-script script.
		 *
		 * @since 2.3.0
		 */

        $shortcodes_gui_translations = apply_filters( 'wpv_filter_wpv_shortcodes_gui_localize_script', $shortcodes_gui_translations );

		wp_localize_script( 'views-shortcodes-gui-script', 'wpv_shortcodes_gui_texts', $shortcodes_gui_translations );

		// Views widget script
		wp_register_script( 'views-widgets-gui-script', WPV_URL_EMBEDDED . '/res/js/views_widgets_gui.js', array( 'jquery', 'suggest' ), WPV_VERSION );

		$widgets_gui_translations = array(
			'ajaxurl'									=> wpv_get_views_ajaxurl()
		);

		wp_localize_script( 'views-widgets-gui-script', 'wpv_widgets_gui_texts', $widgets_gui_translations );

		// Views embedded script
		wp_register_script( 'views-embedded-listing-pages-script', WPV_URL_EMBEDDED . '/res/js/listing_pages.js', array( 'jquery' ), WPV_VERSION, true );
		wp_register_script( 'views-embedded-script', WPV_URL_EMBEDDED . '/res/js/views_embedded.js', array( 'jquery', 'wp-pointer', 'views-codemirror-conf-script' ), WPV_VERSION, true );

		/* ---------------------------- /*
		/* BACKEND STYLES
		/* ---------------------------- */

		// Dialogs styles
		// @todo maybe move to common too
		// Depends on:
		// 		- wp-jquery-ui-dialog
		wp_register_style( 'views-admin-dialogs-css', WPV_URL_EMBEDDED . '/res/css/dialogs.css', array( 'wp-jquery-ui-dialog', 'toolset-dialogs-overrides-css' ), WPV_VERSION );

		// General Views admin style
		// Depends on:
		// 		- wp-pointer
		// 		- font-awesome
		// 		- toolset-colorbox
		// 		- views-admin-dialogs-css
		wp_register_style( 'views-admin-css', WPV_URL_EMBEDDED . '/res/css/views-admin.css', array(
			'wp-pointer', 'font-awesome',
			'toolset-colorbox', 'toolset-select2-css', 'toolset-select2-overrides-css',
			Toolset_Assets_Manager::STYLE_NOTIFICATIONS,
			'views-admin-dialogs-css',
			OTGS_Assets_Handles::POPOVER_TOOLTIP,
		), WPV_VERSION );

		/* ---------------------------- /*
		/* FRONTEND SCRIPTS
		/* ---------------------------- */

		/**
		 * Datepicker localization
		 * Depends on:
		 * 		- jquery
		 * 		- jquery-ui-core
		 * 		- jquery-ui-datepicker
		 *
		 * @note Since WordPress 4.6.0 the jQuery datepicker localization is added automatically
		 *     as an inline script for jquery-ui-datepicker.
		 */
		global $wp_version;
		if ( version_compare( $wp_version, '4.6' ) < 0 ) {

			$lang = get_locale();
			$lang = str_replace( '_', '-', $lang );
			if ( file_exists( WPV_PATH_EMBEDDED . '/res/js/i18n/jquery.ui.datepicker-' . $lang . '.js' ) ) {
				wp_register_script( 'jquery-ui-datepicker-local', WPV_URL_EMBEDDED_FRONTEND . '/res/js/i18n/jquery.ui.datepicker-' . $lang . '.js', array( 'jquery', 'jquery-ui-core', 'jquery-ui-datepicker' ), WPV_VERSION, true );
			} else {
				$lang = substr( $lang, 0, 2 );
				if ( file_exists( WPV_PATH_EMBEDDED . '/res/js/i18n/jquery.ui.datepicker-' . $lang . '.js' ) ) {
					wp_register_script( 'jquery-ui-datepicker-local', WPV_URL_EMBEDDED_FRONTEND . '/res/js/i18n/jquery.ui.datepicker-' . $lang . '.js', array( 'jquery', 'jquery-ui-core', 'jquery-ui-datepicker' ), WPV_VERSION, true );
				}
			}

		}

		// Map script
		// Depends on google-maps
		// For compatibility reasons, we are not registering this unless the Views legacy Maps plugin is enabled.
		if (
			isset( $views_global_settings->wpv_map_plugin )
			&& true == (bool) $views_global_settings->wpv_map_plugin
		) {
			if ( ! wp_script_is( 'google-maps', 'registered' ) ) {
				if ( is_ssl() ) {
					$protocol = 'https';
				} else {
					$protocol = 'http';
				}
				wp_register_script( 'google-maps', $protocol . '://maps.googleapis.com/maps/api/js?sensor=false&libraries=places&ver=3.5.2', array(), null, true );
			}
			wp_register_script( 'views-map-script', WPV_URL_EMBEDDED_FRONTEND . '/res/js/jquery.wpvmap.js', array( 'google-maps', 'jquery' ), WPV_VERSION, true );
		}
	}

	/**
	 * Enqueue the frontend scripts, only when a View has been printed.
	 *
	 * @since 2.8.5
	 */
	public function enqueue_optional_frontend_assets() {
		// Pagination
		// Note that both jquery-ui-datepicker-local and views-pagination-script have jquery-ui-datepicker as dependency
		// Note that since WP 4.6.0 we do not register this locale script anymore
		if ( wp_script_is( 'jquery-ui-datepicker-local', 'registered' ) ) {
			wp_enqueue_script( 'jquery-ui-datepicker-local' );
		}
	}

	/**
	 * Add the frontend styles and scripts.
	 */
	function wpv_frontend_enqueue_scripts() {

		$views_global_settings = WPV_Settings::get_instance();

		// Maps
		if (
			isset( $views_global_settings->wpv_map_plugin )
			&& true == (bool) $views_global_settings->wpv_map_plugin
		) {
			wp_enqueue_script( 'views-map-script' );
		}

	}

	function wpv_admin_enqueue_scripts( $hook ) {

		$page = wpv_getget( 'page' );

		// Assets for the shortcodes GUI
		$force_load_shortcodes_gui_assets = array( 'dd_layouts_edit' );
		$force_load_shortcodes_gui_assets = apply_filters( 'wpv_filter_wpv_force_load_shortcodes_gui_assets', $force_load_shortcodes_gui_assets );
		if (
			$hook == 'post.php'
			|| $hook == 'post-new.php'
			|| in_array( $page, $force_load_shortcodes_gui_assets )
		) {
			if ( ! wp_script_is( 'views-shortcodes-gui-script' ) ) {
				wp_enqueue_script( 'views-shortcodes-gui-script' );
			}
            if ( ! wp_script_is( 'jquery-ui-resizable' ) ) {
				wp_enqueue_script('jquery-ui-resizable');
			}
			if ( ! wp_style_is( 'views-admin-css' ) ) {
				wp_enqueue_style( 'views-admin-css' );
			}
		}

		if ( $page == 'dd_layouts_edit' ) {
			if ( ! wp_script_is( 'views-codemirror-conf-script' ) ) {
				wp_enqueue_script( 'views-codemirror-conf-script' );
			}
			if ( ! wp_style_is( 'toolset-meta-html-codemirror-css' ) ) {
				wp_enqueue_style( 'toolset-meta-html-codemirror-css' );
			}
		}

        // Assets for embedded listing pages
        if( in_array( $page, array( 'embedded-views', 'embedded-views-templates', 'embedded-views-archives' ) ) ) {
            if ( ! wp_script_is( 'views-embedded-listing-pages-script' ) ) {
				wp_enqueue_script( 'views-embedded-listing-pages-script' );
			}
			if ( ! wp_style_is( 'views-admin-css' ) ) {
				wp_enqueue_style( 'views-admin-css' );
			}
		}

		// Assets for embedded edit pages
        if ( in_array( $page, array( 'views-embedded', 'view-templates-embedded', 'view-archives-embedded', 'ModuleManager_Modules' ) ) ) {
			if ( ! wp_script_is( 'views-codemirror-conf-script' ) ) {
				wp_enqueue_script( 'views-codemirror-conf-script' );
			}
			if ( ! wp_style_is( 'toolset-meta-html-codemirror-css' ) ) {
				wp_enqueue_style( 'toolset-meta-html-codemirror-css' );
			}
			if ( ! wp_script_is( 'views-embedded-script' ) ) {
				wp_enqueue_script( 'views-embedded-script' );
			}
			if ( ! wp_script_is( 'views-utils-script' ) ) {
				wp_enqueue_script( 'views-utils-script' );
			}
			if ( ! wp_style_is( 'views-admin-css' ) ) {
				wp_enqueue_style( 'views-admin-css' );
			}
		}

		// Assets for the Widgets page
		if ( $hook == 'widgets.php' ) {
			if ( ! wp_script_is( 'views-widgets-gui-script' ) ) {
				wp_enqueue_script( 'views-widgets-gui-script' );
			}
			if ( ! wp_style_is( 'views-admin-css' ) ) {
				wp_enqueue_style( 'views-admin-css' );
			}
		}

	}

	function wpv_get_force_disable_dps( $status = false ) {
		return $this->get_force_disable_dependant_parametric_search();
	}


	function get_force_disable_dependant_parametric_search() {
		return $this->force_disable_dependant_parametric_search;
	}

	function check_force_disable_dependant_parametric_search() {
		$force_disable = false;
		$view_settings = $this->get_view_settings();
		// Make sure we include ghost query filters from frontend search filters in the Views block.
		$view_settings = apply_filters( 'wpv_filter_object_settings_for_fake_url_query_filters', $view_settings );
		if ( isset( $view_settings['dps'] )
			&& isset( $view_settings['dps']['enable_dependency'] )
			&& $view_settings['dps']['enable_dependency'] == 'enable' )
		{
			$controls_per_kind = wpv_count_filter_controls( $view_settings );
			$controls_count = 0;
			$no_intersection = array();

			if ( !isset( $controls_per_kind['error'] ) ) {
				// $controls_count = array_sum( $controls_per_kind );
				$controls_count = $controls_per_kind['cf'] + $controls_per_kind['tax'] + $controls_per_kind['pr'] + $controls_per_kind['search'];

				if ( $controls_per_kind['cf'] > 1
					&& ( !isset( $view_settings['custom_fields_relationship'] ) || $view_settings['custom_fields_relationship'] != 'AND' ) )
				{
					$no_intersection[] = __( 'custom field', 'wpv-views' );
				}

				if ( $controls_per_kind['tax'] > 1
					&& ( !isset( $view_settings['taxonomy_relationship'] ) || $view_settings['taxonomy_relationship'] != 'AND' ) )
				{
					$no_intersection[] = __( 'taxonomy', 'wpv-views' );
				}
			} else {
				$force_disable = true;
			}

			if ( $controls_count > 0 ) {
				if ( count( $no_intersection ) > 0 ) {
					$force_disable = true;
				}
			} else {
				$force_disable = true;
			}
		}
		$this->set_force_disable_dependant_parametric_search( $force_disable );
		return $force_disable;
	}

	function wpv_force_disable_dps( $state = false ) {
		$this->set_force_disable_dependant_parametric_search( $state );
	}

	function set_force_disable_dependant_parametric_search( $bool = false ) {
		$this->force_disable_dependant_parametric_search = $bool;
	}

	/**
	 * wpv_get_view_url_params TODO
	 *
	 */
	function wpv_get_view_url_params( $id = null ) {
		$view_settings = $this->get_view_settings( $view_id );

	}

	function wpv_get_rendered_views_ids( $used_ids = array() ) {
		return $this->view_used_ids;
	}

	function wpv_get_post_query( $query = null ) {
		return $this->post_query;
	}

	function wpv_get_taxonomy_query( $query = array() ) {
		return $this->taxonomy_data;
	}

	function wpv_get_user_query( $query = array() ) {
		return $this->users_data;
	}

}

/**
 * WPML translate call.
 *
 * @param $name
 * @param string $string
 * @param bool $register
 * @param string $context
 *
 * @return string
 *
 * @todo maybe move to the WPML file
 */
function wpv_translate( $name, $string, $register = false, $context = 'plugin Views' ) {
	if ( !function_exists( 'icl_t' ) ) {
		return $string;
	}

	if ( $register ) {
		icl_register_string( $context, $name, $string );
	}

	return icl_t( $context, $name, stripslashes( $string ) );
}


/**
* wpv_admin_exclude_tax_slugs
*
* Applied in the filter wpv_admin_exclude_tax_slugs, returns an array of taxonomy slugs that are left out in Views taxonomy-related View loops admin GUIs.
*
* We take out taxonomies with show_ui set to false by default, but some custom taxonomies declared for internal use
* by some plugins do not use it. If that is the case and no custom labels are provided, the custom taxonomy hijacks
* Categories or Post Tags in some Views taxonomy-related View loops admin GUIs that rely on the labels.
* This filter takes those internal taxonomies out of our loops.
*
* @param $exclude_tax_slugs (array) The slugs to be excluded.
*
* @return $exclude_tax_slugs
*
* @since unknown
*/

function wpv_admin_exclude_tax_slugs( $exclude_tax_slugs ) {

	// first we exclude the three built-in taxonomies that we want to leave out_items
	if ( ! in_array( 'post_format', $exclude_tax_slugs ) ) {
		$exclude_tax_slugs[] = 'post_format';
	}
	if ( ! in_array( 'link_category', $exclude_tax_slugs ) ) {
		$exclude_tax_slugs[] = 'link_category';
	}
	if ( ! in_array( 'nav_menu', $exclude_tax_slugs ) ) {
		$exclude_tax_slugs[] = 'nav_menu';
	}

	// WP RSS Aggregator issue: https://icanlocalize.basecamphq.com/projects/7393061-toolset/todo_items/171941369/comments
	// Filtering out an internal custom taxonomy with slug wp_log_type

	if ( ! in_array( 'wp_log_type', $exclude_tax_slugs ) ) {
		$exclude_tax_slugs[] = 'wp_log_type';
	}

	return $exclude_tax_slugs;
}

/**
* wpv_admin_exclude_post_type_slugs
*
* Applied in the filter wpv_admin_exclude_post_type_slugs, returns an array of post type slugs that are left out in some database calls.
*
* We are using this, for example, in the target suggest script for parametric search, as we do not want to offer some post types as available targets.
*
* @param $exclude_post_type_slugs (array) The slugs to be excluded.
*
* @return $exclude_post_type_slugs
*
* @since 1.7
*/

function wpv_admin_exclude_post_type_slugs( $exclude_post_type_slugs ) {
	// Exclude al non-public post types
	$exclude_args = array(
	   'public'   => false
	);
	$exclude_output = 'names';
	$exclude_post_types = get_post_types( $exclude_args, $exclude_output );
	foreach ( $exclude_post_types as $exclude_p_t ) {
		if ( ! in_array( $exclude_p_t, $exclude_post_type_slugs ) ) {
			$exclude_post_type_slugs[] = $exclude_p_t;
		}
	}
	// Leave out all the Toolset post types - the above one takes out the Types field groups ones
	if ( ! in_array( 'view', $exclude_post_type_slugs ) ) {
		$exclude_post_type_slugs[] = 'view';
	}
	if ( ! in_array( 'view-template', $exclude_post_type_slugs ) ) {
		$exclude_post_type_slugs[] = 'view-template';
	}
	if ( ! in_array( 'cred-form', $exclude_post_type_slugs ) ) {
		$exclude_post_type_slugs[] = 'cred-form';
	}
	if ( ! in_array( 'dd_layouts', $exclude_post_type_slugs ) ) {
		$exclude_post_type_slugs[] = 'dd_layouts';
	}
	// Also leave out revisions
	if ( ! in_array( 'revision', $exclude_post_type_slugs ) ) {
		$exclude_post_type_slugs[] = 'revision';
	}
	return $exclude_post_type_slugs;
}

/**
 * wpv_admin_include_post_type_slugs
 *
 * Applied in the filter wpv_admin_include_post_type_slugs, returns an array of post type slugs that are included in some database calls.
 *
 * @param $include_post_type_slugs (array) The slugs to be included.
 *
 * @return array $include_post_type_slugs
 *
 * @since 2.3.1
 */

function wpv_admin_include_post_type_slugs( $include_post_type_slugs ) {
    // Include al non-public post types
    $include_args = array(
        'public'   => true
    );
    $include_output = 'names';
    $include_post_types = get_post_types( $include_args, $include_output );
    foreach ( $include_post_types as $include_p_t ) {
        if ( ! in_array( $include_p_t, $include_post_type_slugs ) ) {
            $include_post_type_slugs[] = $include_p_t;
        }
    }

    return $include_post_type_slugs;
}

/**
 * wpv_admin_available_spinners
 *
 * Applied in the filter wpv_admin_available_spinners, returns an array of default available spinners used in pagination and parametric search.
 *
 * Note that this filter is hooked at priority 5 and sets the basic existing spinners, so further spinners should be added at a later priority.
 *
 * @param $available_spinners (array) The spinners to be offered.
 *
 * @return array $available_spinners
 *
 * @since 1.7
 */
function wpv_admin_available_spinners( $available_spinners ) {
	$available_spinners = array(
		array(
			'title'	=> __( 'Spinner #1', 'wpv-views' ),
			'url'	=> WPV_URL_EMBEDDED . '/res/img/ajax-loader.svg'
		),
		array(
			'title'	=> __( 'Spinner #2', 'wpv-views' ),
			'url'	=> WPV_URL_EMBEDDED . '/res/img/ajax-loader2.svg'
		),
		array(
			'title'	=> __( 'Spinner #3', 'wpv-views' ),
			'url'	=> WPV_URL_EMBEDDED . '/res/img/ajax-loader3.svg'
		),
		array(
			'title'	=> __( 'Spinner #4', 'wpv-views' ),
			'url'	=> WPV_URL_EMBEDDED . '/res/img/ajax-loader4.svg'
		),
		array(
			'title'	=> __( 'Spinner #5', 'wpv-views' ),
			'url'	=> WPV_URL_EMBEDDED . '/res/img/ajax-loader-overlay.svg'
		)
	);
	return $available_spinners;
}


/**
 * Return array of possible attributes for view shortcode
 *
 * @param int $view_id The ID of the relevant View.
 *
 * @return int[] Numeric array of possible attributes for $view_id.
 *
 * Output example:
 * 			'query_type' => posts|taxonomy|users
 * 			'filter_type' => filter that this attribute is used on (post_id, post_author, etc..)
 * 			'value' => filter from where attribute getting data
 * 			'attribute' => the actual shortcode attribute
 * 			'expected' => input data type integer|string|numeric
 *
 * Usage example:  <?php print_r( get_view_allowed_attributes( 80 ) ); ?>
 *
 * @todo review the 'value' entry
 */
function get_view_allowed_attributes( $view_id ) {
	$attributes = array();
	if ( empty( $view_id ) ) {
		return $attributes;
	}
	global $WP_Views;
	$view_settings = $WP_Views->get_view_settings( $view_id );
	if (
		is_array( $view_settings )
		&& isset( $view_settings['view-query-mode'] )
		&& $view_settings['view-query-mode'] == 'normal'
		&& isset( $view_settings['query_type'][0] )
	) {
		$query_type = $view_settings['query_type'][0];
		$attributes = apply_filters( 'wpv_filter_register_shortcode_attributes_for_' . $query_type, $attributes, $view_settings );
		// Post View
		if ( $view_settings['query_type'][0] == 'posts' ) {
			foreach ( $view_settings as $key => $value ) {
				// Taxonomy
				if (
					preg_match( "/tax_(.*)_relationship/", $key, $res )
					&& $value == 'FROM ATTRIBUTE'
				) {
					$taxonomy = $res[1];
					if ( taxonomy_exists( $taxonomy ) ) {
						$attributes[] = array(
							'query_type'	=> $view_settings['query_type'][0],
							'filter_type'	=> 'post_taxonomy_' . $taxonomy,
							'filter_label'	=> sprintf( __( 'Post taxonomy - %s', 'wpv-views' ), $taxonomy ),
							'value'			=> $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url-format' ][0],
							'attribute'		=> $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url' ],
							'expected'		=> 'string',
							'placeholder'	=> ( $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url-format' ][0] == 'slug' ) ? 'cat1' : 'Cat 1',
							'description'	=> ( $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url-format' ][0] == 'slug' ) ? __( 'Please type a comma separated list of term slugs', 'wpv-views' ) : __( 'Please type a comma separated list of term names', 'wpv-views' )
						);
					}
				}
				// Custom fields
				if (
					preg_match( "/custom-field-(.*)_value/", $key, $res )
					&& preg_match( "/VIEW_PARAM\(([^\)]+)\)/", $value, $shortcode )
				) {
					$expected_input_data_type = in_array( $view_settings[ 'custom-field-' . $res[1] . '_type' ], array( 'NUMERIC', 'DATE', 'DATETIME', 'TIME' ) )
							? 'integer'
							: ( ( $view_settings[ 'custom-field-' . $res[1] . '_type' ] == 'DECIMAL' ) ? 'decimal' : 'string' );
					$attributes[] = array(
						'query_type'	=> $view_settings['query_type'][0],
						'filter_type'	=> 'post_custom_field_'. $res[1],
						'filter_label'	=> sprintf( __( 'Custom field - %s', 'wpv-views' ), $res[1] ),
						'value'			=> 'custom_field_value',
						'attribute'		=> $shortcode[1],
						'expected'		=> $expected_input_data_type,
						'placeholder'	=> 'value',
						'description'	=> __( 'Please type a custom field value', 'wpv-views' )
					);
				}
			}
		}

		// User View
		if ( $view_settings['query_type'][0] == 'taxonomy' ) {
			foreach ( $view_settings as $key => $value ) {
				// Termmeta fields
				if (
					preg_match( "/termmeta-field-(.*)_value/", $key, $res )
					&& preg_match( "/VIEW_PARAM\(([^\)]+)\)/", $value, $shortcode )
				) {
					$expected_input_data_type = in_array( $view_settings[ 'termmeta-field-' . $res[1] . '_type' ], array('NUMERIC','DATE','DATETIME','TIME') )
							? 'integer'
							: ( ( $view_settings[ 'termmeta-field-' . $res[1] . '_type' ] == 'DECIMAL' ) ? 'decimal' : 'string' );
					$attributes[] = array(
						'query_type'	=> $view_settings['query_type'][0],
						'filter_type'	=> 'taxonomy_termmeta_field_'. $res[1],
						'filter_label'	=> sprintf( __( 'Termmeta field - %s', 'wpv-views' ), $res[1] ),
						'value'			=> 'termmeta_field_value',
						'attribute'		=> $shortcode[1],
						'expected'		=> $expected_input_data_type,
						'placeholder'	=> 'value',
						'description'	=> __( 'Please type a termmeta field value', 'wpv-views' )
					);
				}
			}
		}

		// User View
		if ( $view_settings['query_type'][0] == 'users' ) {
			foreach ( $view_settings as $key => $value ) {
				// Usermeta fields
				if (
					preg_match( "/usermeta-field-(.*)_value/", $key, $res )
					&& preg_match( "/VIEW_PARAM\(([^\)]+)\)/", $value, $shortcode )
				) {
					$expected_input_data_type = in_array( $view_settings[ 'usermeta-field-' . $res[1] . '_type' ], array('NUMERIC','DATE','DATETIME','TIME') )
							? 'integer'
							: ( ( $view_settings[ 'usermeta-field-' . $res[1] . '_type' ] == 'DECIMAL' ) ? 'decimal' : 'string' );
					$attributes[] = array(
						'query_type'	=> $view_settings['query_type'][0],
						'filter_type'	=> 'user_usermeta_field_'. $res[1],
						'filter_label'	=> sprintf( __( 'Usermeta field - %s', 'wpv-views' ), $res[1] ),
						'value'			=> 'usermeta_field_value',
						'attribute'		=> $shortcode[1],
						'expected'		=> $expected_input_data_type,
						'placeholder'	=> 'value',
						'description'	=> __( 'Please type an username field value', 'wpv-views' )
					);
				}
			}
		}
	}

	return $attributes;
}

/**
 * Return array of possible attributes for View URL parameters
 *
 * @param $view_id The ID of the relevant View.
 *
 * @return Numeric array of possible URL parameters for $view_id.
 *
 * Output example:
 * 			'query_type' => posts|taxonomy|users
 * 			'filter_type' => filter that this attribute is used on (post_id, post_author, etc..)
 * 			'value' => filter from where attribute getting data
 * 			'attribute' => the actual url parameter
 * 			'expected' => input data type integer|string|numeric
 *
 * Usage example:  <?php print_r( get_view_allowed_url_parameters( 80 ) ); ?>
 *
 * @todo review the 'value' entry
 */
function get_view_allowed_url_parameters( $view_id ) {
	$attributes = array();
	if ( empty( $view_id ) ) {
		return $attributes;
	}
	global $WP_Views;
	$view_settings = $WP_Views->get_view_settings( $view_id );

	/**
	 * Adjust the View settings just before getting the allowed URL parameters,
	 * to include the JIT defined query filters as companion for search filters.
	 *
	 * @param array $view_settings
	 * @return array
	 * @since 3.0
	 */
	$view_settings = apply_filters( 'wpv_filter_object_settings_for_fake_url_query_filters', $view_settings );

	$query_type = '';

	if (
		is_array( $view_settings )
		&& isset( $view_settings['view-query-mode'] )
	) {
		switch ( $view_settings['view-query-mode'] ) {
			case 'normal':
				if ( isset( $view_settings['query_type'][0] ) ) {
					$query_type = $view_settings['query_type'][0];
				}
				break;
			default:
				$query_type = 'posts';
				break;
		}
	}

	$attributes = apply_filters( 'wpv_filter_register_url_parameters_for_' . $query_type, $attributes, $view_settings );
	$meta_value_pattern = '/URL_PARAM\(([^(]*?)\)/siU';

	switch ( $query_type ) {
		case 'posts':
			foreach ( $view_settings as $key => $value ) {
				// Taxonomy
				if (
					preg_match( "/tax_(.*)_relationship/", $key, $res )
					&& $value == 'FROM URL'
				) {
					$taxonomy = $res[1];
					if ( taxonomy_exists( $taxonomy ) ) {
						$attributes[] = array(
							'query_type'	=> $view_settings['query_type'][0],
							'filter_type'	=> 'post_taxonomy_' . $taxonomy,
							'filter_label'	=> sprintf( __( 'Post taxonomy - %s', 'wpv-views' ), $taxonomy ),
							'value'			=> $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url-format' ][0],
							'attribute'		=> $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url' ],
							'expected'		=> 'string',
							'placeholder'	=> ( $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url-format' ][0] == 'slug' ) ? 'cat1' : 'Cat 1',
							'description'	=> ( $view_settings[ 'taxonomy-' . $taxonomy . '-attribute-url-format' ][0] == 'slug' ) ? __( 'Please type a comma separated list of term slugs', 'wpv-views' ) : __( 'Please type a comma separated list of term names', 'wpv-views' )
						);
					}
				}
				// Custom fields
				if (
					preg_match( "/custom-field-(.*)_value/", $key, $res )
					&& preg_match_all( $meta_value_pattern, $value, $matches_postmeta, PREG_SET_ORDER )
				) {
					$expected_input_data_type = in_array( $view_settings[ 'custom-field-' . $res[1] . '_type' ], array( 'NUMERIC', 'DATE', 'DATETIME', 'TIME' ) )
							? 'integer'
							: ( ( $view_settings[ 'custom-field-' . $res[1] . '_type' ] == 'DECIMAL' ) ? 'decimal' : 'string' );
					foreach( $matches_postmeta as $index => $match ) {
						$attributes[] = array(
							'query_type'	=> $view_settings['query_type'][0],
							'filter_type'	=> 'post_custom_field_'. $res[1] . '_' . $index,
							'filter_label'	=> sprintf( __( 'Custom field - %s', 'wpv-views' ), $res[1] ),
							'value'			=> 'custom_field_value',
							'attribute'		=> $match[1],
							'expected'		=> $expected_input_data_type,
							'placeholder'	=> 'value',
							'description'	=> __( 'Please type a custom field value', 'wpv-views' )
						);
					}
				}
			}
			break;
		case 'taxonomy':
			foreach ( $view_settings as $key => $value ) {
				// Termmeta fields
				if (
					preg_match( "/termmeta-field-(.*)_value/", $key, $res )
					&& preg_match_all( $meta_value_pattern, $value, $matches_termmeta, PREG_SET_ORDER )
				) {
					$expected_input_data_type = in_array( $view_settings[ 'termmeta-field-' . $res[1] . '_type' ], array('NUMERIC','DATE','DATETIME','TIME') )
							? 'integer'
							: ( ( $view_settings[ 'termmeta-field-' . $res[1] . '_type' ] == 'DECIMAL' ) ? 'decimal' : 'string' );
					foreach( $matches_termmeta as $index => $match ) {
						$attributes[] = array(
							'query_type'	=> $view_settings['query_type'][0],
							'filter_type'	=> 'taxonomy_termmeta_field_'. $res[1] . '_' . $index,
							'filter_label'	=> sprintf( __( 'Termmeta field - %s', 'wpv-views' ), $res[1] ),
							'value'			=> 'termmeta_field_value',
							'attribute'		=> $match[1],
							'expected'		=> $expected_input_data_type,
							'placeholder'	=> 'value',
							'description'	=> __( 'Please type a termmeta field value', 'wpv-views' )
						);
					}
				}
			}
			break;
		case 'users':
			foreach ( $view_settings as $key => $value ) {
				// Usermeta fields
				if (
					preg_match( "/usermeta-field-(.*)_value/", $key, $res )
					&& preg_match_all( $meta_value_pattern, $value, $matches_usermeta, PREG_SET_ORDER )
				) {
					$expected_input_data_type = in_array( $view_settings[ 'usermeta-field-' . $res[1] . '_type' ], array('NUMERIC','DATE','DATETIME','TIME') )
							? 'integer'
							: ( ( $view_settings[ 'usermeta-field-' . $res[1] . '_type' ] == 'DECIMAL' ) ? 'decimal' : 'string' );
					foreach( $matches_usermeta as $index => $match ) {
						$attributes[] = array(
							'query_type'	=> $view_settings['query_type'][0],
							'filter_type'	=> 'user_usermeta_field_'. $res[1] . '_' . $index,
							'filter_label'	=> sprintf( __( 'Usermeta field - %s', 'wpv-views' ), $res[1] ),
							'value'			=> 'usermeta_field_value',
							'attribute'		=> $match[1],
							'expected'		=> $expected_input_data_type,
							'placeholder'	=> 'value',
							'description'	=> __( 'Please type an username field value', 'wpv-views' )
						);
					}
				}
			}
			break;
	}

	return $attributes;
}
Page Not Found
Parece que el enlace que apuntaba aquí no sirve. ¿Quieres probar con una búsqueda?
¡Hola!